私のアプリケーションでは、複数の人 (おそらく 5 人以上) が同時にチャットできるライブ チャット機能が必要です。
Java ベースの Google App Engine を使用しています。GAE Datastore を使用するのはこれが初めてです。Oracle/MySQL の使用に慣れているため、私の戦略は間違っていると思います。
注: 簡単にするために、検証/セキュリティ チェックを省略して
います。呼び出された一部のサーブレットWriteMessage
には、次のコードがあります。
Entity entity = new Entity("ChatMessage");
entity.setProperty("userName", request.getParameter("userName"));
entity.setProperty("message", request.getParameter("message"));
entity.setProperty("time", new Date());
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
datastore.put(entity);
と呼ばれる他のサーブレットReadMessages
には、次のコードがあります
String id = request.getParameter("id");
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
Query query = new Query("ChatMessage");
if (id != null) {
// Client requested only messages with id greater than this id
Filter idFilter = new FilterPredicate(Entity.KEY_RESERVED_PROPERTY,
FilterOperator.GREATER_THAN,
KeyFactory.createKey("ChatMessage", Long.parseLong(id)));
query.setFilter(idFilter);
}
PreparedQuery pq = datastore.prepare(query);
JsonArray messages = new JsonArray();
for (Entity result : pq.asIterable()) {
JsonObject jmsg = new JsonObject();
// Client will use this id on the next request to read to poll only
// "new" messages
jmsg.addProperty("id", result.getKey().getId());
jmsg.addProperty("userName", (String) result.getProperty("userName"));
jmsg.addProperty("message", (String) result.getProperty("message"));
jmsg.addProperty("time", ((Date) result.getProperty("time")).getTime());
messages.add(jmsg);
}
PrintWriter out = response.getWriter();
out.print(messages.toString());
JavaScript クライアント コードでは、WriteMessage
サーブレットはユーザーが新しいメッセージを送信するReadMessages
たびに呼び出され、サーブレットは毎秒呼び出されて新しいメッセージを取得します。
最適化するために、javascript は への後続のリクエストで最後に受信したメッセージの ID (またはおそらくこれまでに受信した最大の ID) をReadMessage
送信し、以前に見たことのないメッセージのみが応答に含まれるようにします。
最初はこれですべてうまくいくように見えますが、このコードにはいくつか問題があるのではないかと思います。
ここに私が間違っていると思うものがあります:
ChatMessage のキーの ID に依存して、JS クライアントが以前に見たメッセージを除外しているため、一部のメッセージが読み取られない可能性があります。信頼できるとは思いませんか?
まったく同じ時間に 5 つまたは 6 つの受信書き込みがある可能性があるため、一部の書き込みが失敗する可能性があります
ConcurrentModificationException
。エンティティに渡された日付は、アプリケーション サーバー上の JRE の現在の日付です。SQL で "sysdate()" のようなものを使用する必要があるのではないでしょうか? これが実際に問題であるかどうかはわかりません。
次のようにコードを修正するにはどうすればよいですか。
すべてのチャット メッセージが書き込まれます。リクエストが失敗した場合、javascript が成功するまで再試行するように、フェールオーバーを設定するのが最善でしょうか?
すべてのチャット メッセージが読まれます (例外なし)
1000 程度のメッセージのみが保存されるように、古いメッセージをクリーンアップします。