2013-12-17 23:39:32

Play framework1系をWindowsサービスで動かす方法

テーマ:ブログ
<<17日目 / 19日目 >>
Play framework 2.x Java and 1.x Advent Calendar 2013の18日目の @s_kozake です。

Play frameworkをWindowsサーバーで運用する場合、Windowsサービス化したいです。サーバーブート時にWebアプリケーションを自動起動する為ですが、それ以外にもJP1などの監視ツールでアプリケーションの生存を監視したい場合、プロセス名による監視だとイメージ名が「java」になるのでどれが監視したいアプリケーションか分からなくて困ります。その点、Windowsサービスならサービス名で監視できます。

ま、とにかくPlay1系をWindowsサービス化したいと思った時期があって、1系でそういう記事が見当たらなかったので自分の検討結果を書いてみたいと思います。

アプリケーションサーバで動作させる

はい、一番簡単な方法ですね。
play warコマンドたたいてwarを作ってアプリケーションサーバにdeployすればいいです。実際、以前僕が担当したPlay案件はWeblogic上にwarファイルでdeployして運用しています。

スタンドアロンで動作させる

今回紹介するのはこちらの方法です。winswを使って実現を検討しました。

winswはJenkinsの生みの親、川口耕介さんが作成した、実行プロセスをWindowsサービスとしてラップするツールです。JenkinsやGlassfishサーバーをWindowsサービス化する場合もこのツールが使われていますので、実績は問題なしです。

winswの詳細な使い方は以下のサイトを参照してください。

https://github.com/kohsuke/winsw

では、毎度毎度のplay runコマンドをWindowsサービス化する手順を以下に示します。

1.helloアプリを作成

とりあえず、Windowsサービスで動かすWebアプリケーションを作成します。
今回は簡単に
play new hello

でhello world!を表示させるだけのWebアプリケーションを作成しました。

2.winswを入手

以下のサイトからwinswを入手してください。記事を書いた時点の最新は1.13でした。

http://repo.jenkins-ci.org/releases/com/sun/winsw/winsw/

入手したファイルをWebアプリケーションのフォルダ直下に配置し、exeファイルをhello.exeにリネームしてください。

3.環境ファイルの作成

helloフォルダにhello.xmlという環境ファイルを作成します。
この記述がwinswへの動作を指定します。
<service>
<id>hello</id>
<name>Hello</name>
<description>Hello Play app.</description>
<executable>%PLAY_HOME%\play.bat</executable>
<startargument>run</startargument>
<logmode>rotate</logmode>
</service>

4.Windowsサービスへの登録

Webアプリケーションのフォルダで以下のコマンドを実行すれば、Windowsサービスへ登録できます。
hello install

サービス登録を解除する場合は以下のコマンドです。
hello uninstall

5.Windowsサービス起動

サービス画面で起動すればokです。
以下のコマンドでも起動できます。
hello start

停止は以下のとおり
hello stop

6.サービス停止

実はこのままではWindowsサービスを停止してもWebアプリケーションは停止しません。ですので、Playアプリケーションを停止させる手段が必要です。

play start/stopコマンドみたいにserver.pidを出力して、pidに対してkillを発行するようにしてもいいのですが、この方法だとWebアプリケーションがプロセス強制終了するので、@OnApplicationStopアノーテーションで指定したジョブが動作しなかったり、実行中の処理の有無関係なくアプリケーションが強制終了するので、安全な停止を行いたい場合はあまりいい手段とはいえません。

ですので、シークレットキーを指定したHTTP GET要求を発行すれば終了できるようにします。

コントローラに以下のアクションを追加し、
  public static void stop(String id) {
if ("SECRET".equals(id)) {
Logger.info("shutdown processing write here...");

System.exit(0);
}
}

routesに以下のルーティングを追加します。
GET     /stop/{id}                              Application.stop

これで、
http://127.0.0.1/stop/SECRET

を発行すれば、アプリケーションを停止することが出来ます。
stopメソッドの中で、安全に処理を終了させれば問題ないと思います。

また、@OnApplicationStopアノーテーションで指定したジョブは内部的にPlay.stop()メソッドから呼ばれていますが、Play.stop()メソッドはシャットダウンフックに登録されているので、System.exit(0)を発行後、実行されます。
  Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
Play.stop();
}
});

あとは、このGETコマンドを発行できればいいのですが、MS-DOSにはwgetがない。。

ですので、今回は適当に以下のような適当なJavaソースを作成して、

public class ShutdownKick {
public static void main(String[] args) throws Exception {
if (args.length > 0) {
new java.net.URL(args[0]).openConnection().getInputStream();
}
}
}

以下のようにhello.xmlのstopexecutableタグとstopargumentタグを追加してWindowsサービスを登録しなおせば、Windowsサービスの停止でアプリケーションが停止できるようになりました。
<service>
<id>hello</id>
<name>Hello</name>
<description>Hello Play app.</description>
<executable>%PLAY_HOME%\play.bat</executable>
<startargument>run</startargument>
<stopexecutable>%JAVA_HOME%\bin\java</stopexecutable>
<stopargument>-cp</stopargument>
<stopargument>.</stopargument>
<stopargument>ShutdownKick</stopargument>
<stopargument>http://127.0.0.1:9000/stop/SECRET</stopargument>
<logmode>rotate</logmode>
</service>


Play1系のWindowsサービス化の検討については以上です。

今回紹介した全リソースはgithubにあげていますので、必要な方はそちらをご参照ください。


結論

Linux使えば?

<<2013/12/20 追記>>



うらがみさんにめっちゃ怒られたので、終了方法をGETからPOSTに変更しました orz
ちなみに、僕は勉強会でうらがみさんに会うたびに怒られてる気がします。
POST    /stop/                                  Application.stop

<service>
<id>hello</id>
<name>Hello</name>
<description>Hello Play app.</description>
<executable>%PLAY_HOME%\play.bat</executable>
<startargument>run</startargument>
<stopexecutable>%JAVA_HOME%\bin\java</stopexecutable>
<stopargument>-cp</stopargument>
<stopargument>.</stopargument>
<stopargument>ShutdownKick</stopargument>
<stopargument>SECRET</stopargument>
<logmode>rotate</logmode>
</service>

import java.io.*;
import java.net.*;

public class ShutdownKick {
public static void main(String[] args) throws Exception {
if (args.length > 0) {
URL url = new URL("http://127.0.0.1:9000/stop/");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setRequestMethod("POST");
String parameterString = new String("id=" + args[0]);
PrintWriter printWriter = new PrintWriter(connection.getOutputStream());
printWriter.print(parameterString);
printWriter.close();
connection.getInputStream();
connection.disconnect();
}
}
}

gethubの修正差分はこちらを見てね!

AD
いいね!した人  |  コメント(0)  |  リブログ(0)

kozakeさんの読者になろう

ブログの更新情報が受け取れて、アクセスが簡単になります

最近の画像つき記事
 もっと見る >>

コメント

[コメントをする]

コメント投稿

AD

ブログをはじめる

たくさんの芸能人・有名人が
書いているAmebaブログを
無料で簡単にはじめることができます。

公式トップブロガーへ応募

多くの方にご紹介したいブログを
執筆する方を「公式トップブロガー」
として認定しております。

芸能人・有名人ブログを開設

Amebaブログでは、芸能人・有名人ブログを
ご希望される著名人の方/事務所様を
随時募集しております。