Akka モジュールの説明で、Playは優れた Comet をサポートしていると書かれているのを見ましたが、Comet を使用したことがなく、Play のドキュメントで言及されていません。Play ではどのように機能しますか?
私はこれを理解するのに 2 日間かけて数時間を費やしたので、他の Play 初心者のためにこの情報を共有したいと思いました。
Play には、Comet の使用方法を示すサンプル チャット アプリケーションが含まれています。ただし、この例では何が起こっているのかを説明していません。
あなたが送信した新しいアップデートを他の人が見つけられるようにするには、それらをどこかに保存する必要があります。おそらく、これはキャッシュまたはコントローラー自体にある可能性がありますが、データベースが最も安全な方法になるため、モデルが必要になります。また、どの更新が新しいかを判断する方法も必要になります。つまり、おそらく日付フィールドが必要になるでしょう (参照: JPA を使用した最終更新のタイムスタンプ)。Chat の例では、単純なモデルを使用しています。
@Entity
public class Message extends Model {
public String user;
public Date date;
public String text;
public Message(String user, String text) {
this.user = user;
this.text = text;
this.date = new Date();
}
}
コメットを容易にするために、コントローラーには 2 つのメソッドが必要です。新しいデータが投稿される場所で、特別なことは何もしません:
public static void postMessage(String message) {
new Message(session.get("nick"), message).save();
}
もう 1 つは更新を取得するためのものです。
public static void newMessages() {
List<Message> messages = Message.find("date > ?", request.date).fetch();
if (messages.isEmpty()) {
suspend("1s");
}
renderJSON(messages);
}
ここで重要なのsuspend("1s")
は、1 秒に 1 回新しいデータをチェックして、HTTP 要求を開いたままにしておくものです。
ビューには、新しいデータの送信、更新のフェッチ、およびそれらの更新のレンダリングという 3 つの役割があります。
送信は、対応するコントローラー アクションと同様に、特別なことは何もしません。
$('#send').click(function(e) {
var message = $('#message').val();
$('#message').val('');
$.post('@{postMessage()}', {message: message});
});
更新のフェッチは魔法のビットです。
// Retrieve new messages
var getMessages = function() {
$.ajax({
url: '@{newMessages()}',
success: function(messages) {
$(messages).each(function() {
display(this);
});
},
complete: function() {
getMessages();
},
dataType: 'json'
});
}
getMessages();
getMessages()
開始するために一度呼び出され、その後、リクエストが成功するたびに再帰的に呼び出されます。新しいメッセージを探すnewMessages()
アクションを取得し、メッセージがない場合は、何か報告するまでリクエストを開いたままにします。display
新しいメッセージが見つかると、JSON データが関数に渡されます。
var display = function(message) {
$('#thread').append(tmpl('message_tmpl', {message: message}));
}
このdisplay
関数は、JavaScript マイクロ テンプレートを JSON データに適用して、新しいメッセージをレンダリングします。マイクロ テンプレートの使用は必須ではありませんが、かなりうまく機能します。それらは、それらを使用するページのテンプレートに含まれています。
<script type="text/html" id="message_tmpl">
<div class="message <%= message.user == '${session.nick}' ? 'you' : '' %> <%= message.user == 'notice' ? 'notice' : '' %>">
<h2><%= message.user %></h2>
<p><%= message.text.replace('\n', '<br/>') %></p>
</div>
</script>
これtype="text/html"
により、ブラウザー、検索エンジン、およびスクリーン リーダーがscript
ブロック全体を無視します。その結果、jQuery を使用してノードを作成したり、文字列を連結したりするよりも、読みやすく維持しやすくなります。どのビットが関連しているかがわかれば、全体としては非常に単純です。
Web アプリケーションでサーバー プッシュまたはコメットを実現する方法は多数ありますが、最も一般的な方法の 1 つは、最新のブラウザーで十分にサポートされているロング ポーリングです。
Play は、主にsuspend(time);
関数によってロング ポーリングを実現します。この関数は 2 つの非常に重要なことを行います
HTTP 要求を開いたままにして、指定された時間内にアクションを再試行します。これにより、ブラウザに通知したい何かが発生するまで、HTTP リクエストを保留してアクションを再試行し続けることができます。
そして非常に重要なことは、リクエストが中断されるとスレッドが解放されることです。これは、http 要求が、中断された要求ごとに開いているスレッドを保持しないことを意味します。
play が DEV モードの場合、シングル スレッドでのみ実行されますが、サーバーがハングしたり、ユーザーがブロックされた要求を受けたりすることなく、サンプル チャット アプリケーションで数十人のユーザーを実行できます (私も試しました)。
ただし、サスペンド メソッドは、Play が Comet を支援するために実際に行う唯一のものです。サンプル チャット アプリケーションを実行し、クライアントを開いたままにしておくと、120 秒後に要求がタイムアウトすることがわかります。サンプル アプリケーションは、要求を再試行しません。ロング ポーリング テクノロジのクライアント側は、自分で構築する必要があります。
Guillaume はフォーラムで、サンプル チャット アプリケーションは、ポーリングを使用できる期間のデモにすぎないと述べています。したがって、Play が優れた Comet のサポートを証明できるとは思いません。これは正しい方向への一歩にすぎません。
Servlet 3.00 または Jetty7 Continuations に裏打ちされたサーバープッシュ (Comet) に Akka で Mist を使用できます: http://doc.akkasource.org/http#Mist%20-%20Lightweight%20Asynchronous%20HTTP