web-dev-qa-db-ja.com

設計:HTTPとWebsocketが連携して動作する

私はここで少し岐路に立っているので、コミュニティの意見を聞きたいと思いました。

弊社用のチャットアプリを作成しています。これで、他のすべてのチャットアプリケーションと同様に、メッセージはサーバーに保存され、クライアントは後で古いメッセージにアクセスできるようになります。

私が迷っているのは、特定の状況でHTTPWebSocketを一緒に使用することです。私はすでにそれらを使用しています、完全にうまく、すべてがスムーズに実行されています。私の現在のデザインの選択はこれです:

  1. クライアントは、WebSocketを介してnew messageイベントをメッセージペイロードとともにサーバーに送信します
  2. サーバーはこのnew messageイベントを受信します
  3. サーバーは、必要なメッセージとともにメッセージを保存するasyncサーバーにmongodb呼び出しを行います。
  4. DBに対してasync呼び出しを行うと、提供されたメッセージとともに他のクライアントにソケットイベントも送信します。

私が見ている問題は、DBへのasync呼び出しが失敗し、DBが実際にメッセージを保存しない場合はどうなるかということです。クライアントはメッセージを受け取る場合がありますが、ログアウトして再度ログインすると、メッセージはDBに保存されなかったため、会話に存在しなくなります。

幸いなことに、これはテストでは発生していませんが、世界中ですべてのテストを実行できることは誰もが知っています。どういうわけか何かが壊れます。この問題について考えている間、私は問題の新しい解決策を思いつきました:

  1. クライアントはメッセージを作成し、サーバーにPOSTリクエストを送信します
  2. サーバーはこのPOSTリクエストを受信します
  3. DBに対してasyncを指定してawait呼び出しを行います
  4. Promiseを使用して、操作が成功するか失敗するかのいずれか
  5. 成功すると、ソケットイベントが他のクライアントに送信され、メッセージを送信したクライアントは、サーバーから成功したという応答を受信します。
  6. 失敗した場合、クライアントに通知され、ソケットイベントは送信されません

すべてのメッセージでHTTP呼び出しを行う場合と比較して、メッセージが前後に送信されるため、持続的接続を維持することでオーバーヘッドが少なくなることを知っていますが、HTTP呼び出しにより、クライアントはメッセージを確認できます。実際に送信され、保存されます。

これで、async/awaitを使用して元のアイデアを新しいアイデアに変換できます。失敗した場合は、メッセージをまったく送信しないでください。 errorとして出力する新しいソケットイベントを作成すると、クライアントはそれに応じてそれを表示します。

どちらが良いか判断できないので、これらの方法の1つが優れているかどうか、またはこの特定のアプリケーション設計にさらに優れたオプションがあるかどうかを尋ねたいと思いました。

ありがとう!

1
Landon

両方を組み合わせます。つまり、3番目のオプションです。 WebSocketメッセージを保持しますが、クライアントに通知する前にデータベースの結果を待つことにより、操作の順序を順番に並べます。

擬似コード:

function on_new_message(sender, msg):
  is_duplicate = await save_message_in_db_async(msg)
  // If this fails, catch the exception in some error handler.
  // The client could also retry
  // if they don't receive a reply within some timeout.
  // The DB can tell you whether this is a duplicate message.

  // At this point the message is saved.
  // new clients that join can load it from the DB

  sender_notification_future = notify_client_async(sender, {
      "type": "message_acknowledged",
      "msg_id": msg.id,
  })

  client_notification_futures = []
  if !is_duplicate:
    for client in connected_clients:
      if client != sender:
        client_notification_futures.Push(
            notify_client_async(client, msg))

  // The order of notifications is not important,
  // so await them together
  await all(sender_notification_future, client_notification_futures)

または、2番目のオプション(メッセージの送信にPOSTリクエストを使用)を使用する)は、メッセージの送信頻度が低い(最大で数秒に1回)か、HTTPを使用している場合でも問題ありません。/2. POSTリクエストは明確なレスポンスを取得するのに対し、WebSocketはリクエスト/レスポンスのライフサイクルを課さないため、これは簡単かもしれません。コードは基本的に同じですが、送信者にレスポンスする点が異なります。それらに通知を送信する代わりに。

1
amon