コンピューターサイエンス:アルゴリズム / OS / ネットワーク / DB
WebSocket とリアルタイム通信
3 秒ごとに「新着はありますか」と聞き続ける
チャットを作るとき、いちばん素直な作りは、一定間隔でサーバーに問い合わせることです。動きはしますが、無駄が目立ちます。
新着が無くても、ヘッダーだけで数百バイトのやりとりが発生します。100 人が 3 秒ごとに聞けば、毎秒 33 回のほぼ空振りが積み上がります。それでいて、届くのは最大 3 秒遅れです。間隔を詰めれば遅れは減りますが、無駄はそのぶん増えます。
問題は、クライアントから聞かないと始まらない、という前提のほうにあります。
1 回つないだら、切らずに使い回す
WebSocket は、最初だけ普通の HTTP でつなぎ、そこから通信の形を切り替えます。
プレーンテキスト
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgradeサーバーが承諾すると 101 Switching Protocols を返し、以降この接続は HTTP をやめます。同じ 1 本のつながりの上で、どちらからでも好きなときにメッセージを送れる状態になります。
新着が出た瞬間にサーバーから押し出せるので、遅れが無くなります。1 通あたりに付く管理情報も数バイト程度で、聞き続けていたときの数百バイトとは比べものになりません。
なお、この切り替えを通してくれない経路があります。途中の装置が HTTP しか想定していないと、切り替えの依頼を落としたり、途中で接続を打ち切ったりします。暗号化して使う指定にしておくと、途中からは中身が見えないので余計な干渉を受けにくくなります。開発中は動いたのに社内ネットワークからだけつながらない、という報告が来たら、まずここを疑います。
何もしないでいると、いつの間にか切られている
つなぎっぱなしにすると、別の問題が出ます。途中の装置は、しばらく何も流れない接続を掃除します。数分沈黙していただけで切られることがあり、しかも切られたことにこちらは気づきません。
対策は 2 つ組み合わせます。1 つは、生存確認の小さなメッセージを定期的に往復させて、沈黙を作らないことです。もう 1 つは、切れる前提で再接続を書いておくことです。
再接続で気をつける点が 2 つあります。切れた瞬間に全員が一斉につなぎ直すとサーバーが落ちるので、待ち時間を 1 秒、2 秒、4 秒と伸ばしながら試します。そして、切れていた間のメッセージは届いていません。つなぎ直した後に、取りこぼした分を別途取り直す作りが要ります。
押し出すだけでよいなら、もっと軽い手がある
双方向が本当に必要かは、先に確認する価値があります。通知や進捗の表示のように、サーバーから送るだけでよい場面は多いからです。
その場合は Server-Sent Events という選択肢があります。普通の HTTP のレスポンスを閉じずに流し続けるだけの仕組みで、扱いは HTTP のままです。切れたときの再接続もブラウザ側が面倒を見ます。送る向きが片方だけなら、こちらのほうが持ち込みやすいです。
現場の話
つなぎっぱなしということは、利用者の数だけ接続が居座るということです。何もしていない時間もメモリを占め続けるので、1 台あたり何本まで持てるかが上限になります。台数を増やす場合も、A さんが 1 台目、B さんが 2 台目につながっていると、そのままでは互いのメッセージが届きません。台の間でメッセージを配る仕組みを別に用意することになります。
復習ミニクイズ
WebSocket の接続開始時に使われる HTTP の仕組みはどれですか