COLUMN

【テックメモシリーズ】AWSでチャット機能を構築してみた!

NTT東日本が行ってきた さまざまな開発テクニックを紹介する「テックメモシリーズ」、開発者が実際の業務のなかで書き溜めていった作業メモをそのままリアルにご紹介するシリーズとなります。

今回は、社内のオンラインでの配信イベントを担当することになり、動画を視聴しながらWEB上でチャットできる環境をAWSで構築しました。

このコラムでは、チャットの基本的なAWS構成および構築してみてわかったことをご紹介していきます。

チャット機能の構成

ライブ配信で利用するチャットということもあり、利用人数が想定できなかった為、柔軟に拡張可能なサーバーレスのアーキテクチャにて構築しました。

今回弊社では、チャット機能を実現する為に、AWSのWebSocketAPIを利用しました。WebSocketは複数のクライアントとサーバ間でリアルタイムに情報が同期される仕組みを実現するプロトコルです。WebSocketAPIはユーザーがAPIに接続するとConnectionIDを払い出します。このConnectionIDを利用して、メッセージを接続中のユーザーに送ることでリアルタイムにメッセージをやり取りできる機能を実現しています。

使用している各リソースを簡単にご紹介します。上記で述べたAPIは窓口の役割を果たしています。窓口には担当窓口(AWSのドキュメントでは『ルート』と呼ばれます)を複数おくことができ、ユーザー側で送る情報に応じて事前に決めた処理を実行できます。ご紹介した構成図には担当窓口を3個($connect / $sendMessage / $disconnect)置いています。$connect および$disconnectはデフォルトで用意され、$connectはユーザーがWebSocketAPIに接続したときの処理を指定する窓口、$disconnectはユーザーがWebSocketAPIから切断した時の処理を指定する窓口となっています。$sendMessageは、メッセージが送信されたときに動く処理を指定するために追加で作成した窓口(『カスタムルート』と呼ばれます)です。

APIの各担当窓口はそれぞれLambdaを起動させます。Lambdaはサーバーのプロビジョニングや管理をすることなく、コードを実行できるサービスです。DBへのデータの書き込みや取得、メール送信等ちょっとした処理に便利なサービスとなっています。

接続中のユーザーにメッセージを送信するために重要なConnectionID格納するDBはDynamoDBにしました。DynamoDBはハイパフォーマンスなアプリケーションをあらゆる規模で実行するために設計された、フルマネージドでサーバーレスの key-value NoSQL データベースです。

なお、上記で担当窓口を複数おくことができると述べたように、カスタムルートは複数作成することができます。ブラウザで『送信』というボタンを押したときには、$sendMessageルートに紐づけられた処理を、『いいね』というボタンを押したときには$sendLike等の他のルートに紐づけられた処理を実行できます。このようにルート(担当窓口)を判別できるようにするには、API側のルート選択式とブラウザ側でAPIに向かって送信するJson形式のメッセージを工夫しないといけないので注意してください!詳細は、下記ブログ記事が分かりやすいので、よろしければご参照ください。

参照:AWS Lambda で遊ぼう(第10話: WebSocket篇③ - リアルタイムチャット完成篇)

バックエンド処理の流れを見てみよう!

①API接続時

ユーザーがWebSocketAPIに接続すると、ConnectionIDが払い出されます(connection_id:aaaa)。UserAに何か情報を送るには、このConnectionIDが必須情報となりますので、接続用Lambdaでは、DBにConnectionIDを保存する処理をしています。

※複数人接続された場合は下記のような状態になります。

②ユーザー側のメッセージ送信

サーバーでメッセージ受信時には$sendMessageのルートに指定されているメッセージ送受信用Lambdaが動きます。このLambdaで送られてきたJson形式のデータからメッセージ部分(『Hello』)を抽出し、DBに格納されているConnectionIDをScanして、それぞれのConnectionIDに対してAが送信した『Hello』を送信するようにします。

このようにして、ユーザーAにもBにもCにも『Hello』が表示され、リアルタイムチャット機能が実現します。

③API切断時

ブラウザを閉じる等によってAPIとのセッションが切れるわけですが、切れたセッションのConnectionIDがずっとDBに残っていると、エラーが生じてしまいます。そこで、切断時に動くLambdaではコネクションID記録用DBからConnectionIDを削除するような処理を書きましょう。

構築してみてわかったこと

上記を構築していく中で、メッセージを送っても反映されないことがあることに気づきました。切り分けをしていくと、バックエンド自体が動いていないようだったので、ブラウザの検証画面のConsoleを確認してみたところ下記のようなエラーが出ていることに気づきました。

エラー調査を進める中で、WebSocketAPIの上限値があることがわかりました。弊社では、6時間はチャットを維持し続けなければいけなかったので、赤枠で囲った「WebSocketAPIの接続時間」と「アイドル接続のタイムアウト」に対応する必要がありました。

「アイドル接続のタイムアウト」への対応

こちらは10分間メッセージのやり取りが発生しないとセッションが切れてしまうという上限でした。いろいろなブログ記事や担当内の有識者の経験を参考に、9分ごとにユーザーから見えない部分でブラウザからAPIにpingを打って、サーバーからブラウザにpongを返す処理を組み込みました。

「WebSocketAPIの接続時間」への対応

こちらはアイドルタイムを解決して接続が維持されたとしても、WebSocketAPI は最大2時間までしかセッションを維持できないという上限でした。この上限に対しては切断されたら自動的に再接続できるようにブラウザ側で対応しました。ユーザー側には影響が出ないようにしていますが、実はConnectionIDが変わっている状態となります。

最後に

弊社では上記のシンプルなチャット機能を応用し、『いいね』や『リアルタイム視聴者数』、『累計視聴者数』も同じWebSocketAPIで構築していきました。また、LambdaやDBの構成を工夫し、一つのAPIで400を超えるチャットスペースを提供しました。可能性は無限大です、チャット機能を内製開発しないといけない!等の機会がありましたら、ぜひAWSでのチャット機能構築をご検討ください!

実際の配信の様子

おわりに

今回はAWSでチャットを実現する手段としてAmazon API Gateway の Websocket APIを利用した実現方法についてご紹介しました。

AWS LambdaやDynamoDBと組み合わせてどのようにAWSでチャットを実現させているのか、どのような流れで処理されているのか、Websocket APIでチャットを行う上での問題点は何か、またその解決方法とはどのようなものかを詳しく理解できたと思います。

これからAWSでチャットを使ってみたいという方の参考になれば幸いです。

これからもNTT東日本が行ってきましたさまざまな開発テクニックを紹介していきますのでご期待ください。

  • Amazon Web Services(AWS)および記載するすべてのAmazonのサービス名は、米国その他の諸国における、Amazon.com, Inc.またはその関連会社の商標です。

ページ上部へ戻る

相談無料!プロが中立的にアドバイスいたします

クラウド・AWS・Azureでお困りの方はお気軽にご相談ください。