Java および Play Framework での Heroku の WebSocket の使用
最終更新日 2022年02月09日(水)
このチュートリアルでは、WebSocket を使用する Java および Play Framework アプリケーションを構築して Heroku にデプロイする方法を示します。
デモアプリケーションのサンプルコードは GitHub で入手できます。編集や機能強化を歓迎します。単にリポジトリをフォークし、変更を加えて、プルリクエストを送信してください。
前提条件
- Java、Play Framework 2.x、および Heroku CLI (HerokuCLI の設定に関する記事)で説明しているとおり)
- Heroku ユーザーアカウント。 無料ですぐにサインアップできます。
WebSocket アプリの作成
このサンプルアプリケーションは、Java および Play で WebSocket を使用する単純な例を提供します。サンプルを複製し、読みながらコードを追っていくことができます。自分自身でアプリを記述する場合、作業中にサンプルコードを新しい Play アプリに追加することができます。
オプション 1。サンプルアプリを複製する
早く使用開始したい場合は、サンプルアプリを複製するだけで済みます。
$ git clone git@github.com:heroku-examples/play-websockets-sample.git
Cloning into 'play-websockets-sample'...
remote: Counting objects: 31, done.
remote: Compressing objects: 100% (24/24), done.
remote: Total 31 (delta 0), reused 31 (delta 0)
Receiving objects: 100% (31/31), 38.33 KiB | 0 bytes/s, done.
Checking connectivity... done
オプション 2。新しい Play アプリを作成する
$ play new
_
_ __ | | __ _ _ _
| '_ \| |/ _' | || |
| __/|_|\____|\__ /
|_| |__/
play 2.2.0 built with Scala 2.10.2 (running Java 1.6.0_51), http://www.playframework.com
The new application will be created in /Users/jsimone/dev/supportApps/play22test
...
アプリケーション名と、言語として Java を選択します。
機能
このサンプルアプリケーションは、バックエンドへの WebSocket を開く単純な Web ページをレンダリングします。サーバーは、時間を含むペイロードを WebSocket 経由で 1 秒に 1 回送信します。その時間がページに表示されます。
ここで発生する対話には、3 つの重要な部分があります。WebSocket オブジェクトを返すコントローラーメソッド、その WebSocket を開く JavaScript メソッド、その WebSocket を介してペイロードを毎秒送信する Akka アクターです。それぞれを見ていきましょう。
コントローラー
Play コントローラーメソッドから WebSocket を返すことができます。
サンプルアプリケーションの /app/controllers/Application.java
に例があります。
public static WebSocket<String> pingWs() {
return new WebSocket<String>() {
public void onReady(WebSocket.In<String> in, WebSocket.Out<String> out) {
final ActorRef pingActor = Akka.system().actorOf(Props.create(Pinger.class, in, out));
final Cancellable cancellable = Akka.system().scheduler().schedule(Duration.create(1, SECONDS),
Duration.create(1, SECONDS),
pingActor,
"Tick",
Akka.system().dispatcher(),
null
);
in.onClose(new Callback0() {
@Override
public void invoke() throws Throwable {
cancellable.cancel();
}
});
}
};
}
public static Result pingJs() {
return ok(views.js.ping.render());
}
public static Result index() {
return ok(index.render());
}
このメソッドは、そのペイロードとして String を持つ新しい WebSocket オブジェクトを返します。この WebSocket オブジェクトでは、Akka スケジューラー経由でアクターと通信するための onReady()
メソッドを定義します。ソケット経由でデータを送信する処理はそのアクターで発生します。WebSocket が閉じられると、入力ストリームに登録されたコールバックが呼び出されます。
他のメソッドは、js
および html
テンプレートをレンダリングします。
これらのメソッドのルートを routes
ファイルで設定する必要もあります。
# Home page
GET / controllers.Application.index()
GET /pingWs controllers.Application.pingWs()
GET /assets/javascripts/ping.js controllers.Application.pingJs()
モデル
コントローラーの例では、WebSocket の in
および out
ストリームを順番に処理します。アクターでは、他の IO ストリームと同じように、これらのストリームの読み取りと書き込みができます。次に示すのは、Pinger
アクターのコード (/app/models/Pinger.java
) です。
package models;
public class Pinger extends UntypedActor {
WebSocket.In<String> in;
WebSocket.Out<String> out;
public Pinger(WebSocket.In<String> in, WebSocket.Out<String> out) {
this.in = in;
this.out = out;
}
@Override
public void onReceive(Object message) {
if (message.equals("Tick")) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Calendar cal = Calendar.getInstance();
out.write(sdf.format(cal.getTime()));
} else {
unhandled(message);
}
}
}
このアクターは、コントローラーメソッドで定義されたスケジュールに従って “Tick” メッセージを毎秒送信します。そのとき、WebSocket 経由で現在の日付と時刻を送信します。
ビュー
最後の部分は、WebSocket を呼び出すクライアントコードです。このために、サンプルアプリケーションでは Scala テンプレートを使用して、それぞれ /app/views/ping.scala.js
および /app/views/index.scala.html
という名前の JavaScript と HTML をレンダリングします。
index.scala.html
は、その中にデータを表示する div
を提供し、JavaScript を参照します。
@main("Welcome to Play") {
<strong>Stats</strong><br>
<div id="ping"></div>
<script type="text/javascript" charset="utf-8" src="@routes.Application.pingJs()"></script>
}
ping.scala.js
は WebSocket に接続し、表示された div
に遭遇したらそこに日付を入力するための receiveEvent
メソッドを定義します。
$(function() {
var WS = window['MozWebSocket'] ? MozWebSocket : WebSocket
var dateSocket = new WS("@routes.Application.pingWs().webSocketURL(request)")
var receiveEvent = function(event) {
$("#ping").html("Last ping: "+event.data);
}
dateSocket.onmessage = receiveEvent
})
デプロイ
ここで、アプリを Heroku にデプロイします。まだ実行していない場合は、そのアプリケーションを Git リポジトリに配置します。
$ git init
$ git add .
$ git commit -m "Ready to deploy"
デプロイ先の Heroku アプリを作成します。
$ heroku create
Creating high-lightning-129... done, stack is heroku-18
http://high-lightning-129.herokuapp.com/ | git@heroku.com:high-lightning-129.git
Git remote heroku added
git push
でコードをデプロイします。
$ git push heroku master
Counting objects: 31, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (24/24), done.
Writing objects: 100% (31/31), 38.33 KiB | 0 bytes/s, done.
Total 31 (delta 0), reused 0 (delta 0)
-----> Play 2.x - Java app detected
-----> Installing OpenJDK 1.6...done
-----> Building app with sbt
-----> Running: sbt clean compile stage
Getting org.scala-sbt sbt 0.13.0 ...
downloading http://s3pository.heroku.com/ivy-typesafe-releases/org.scala-sbt/sbt/0.13.0/jars/sbt.jar ...
[SUCCESSFUL ] org.scala-sbt#sbt;0.13.0!sbt.jar (416ms)
...
[info] Done packaging.
[success] Total time: 7 s, completed Sep 29, 2013 7:51:08 PM
-----> Dropping ivy cache from the slug
-----> Discovering process types
Procfile declares types -> web
-----> Compiled slug size: 117.3MB
-----> Launching... done, v6
http://still-hamlet-4310.herokuapp.com deployed to Heroku
以上で、Web アプリが Heroku で稼働するようになりました。 アプリケーションにアクセスして動作を確認してみましょう。
$ heroku open
スケーリング
このアプリは、WebSocket の単純な使用法を示しています。本番環境での使用、および複数の web
dyno が必要なアプリについては、WebSocket を使用したスケーラブルなアプリの構築に関する記事をお読みください。
リファレンス
- WebSocket のドキュメント