🐣

これからWebSocketを導入しようと考える前に読んでほしい

2021/03/18に公開

こんにちはハトです。業務でwebアプリにWebSocketを導入し、半年たったので知見を共有したいと思います。間違ったことを言っている可能性がありますご了承ください(指摘などwelcomeです)

ぶっちゃけリアルタイム通信ってそこまで必要ない

後述しますが、WebSocketを導入すると同時に複雑さも入り込みます。WebSocketは必要ないのであれば入れないほうが良いと思います。自身のサービスの要件を考えたときにリアルタイム通信が本当に必要なのかどうか考えましょう。

WebSocketを導入するとスケールアウト時に、クライアントが接続しているサーバー間をまたぐ通信にRedisなどを導入する必要があります。

WebSocketとsocket.ioの違い

最新のブラウザはWebSocket通信に対応しています。しかしWebSocketについて調べると有名なsocket.ioというライブラリが出てきます。主な違いは

  • socket.ioはサーバーがwebsocket通信に対応していなくても、ロングポーリングを自動的にやってくれる。
  • socket.ioにはroom機能がある。チャットアプリのグループのように使いたいときに便利。

といった点です。最小限の構成でWebSocketを使いたい場合は、socket.ioは特に必要ありません。

もしsocket.ioを利用する場合は、ネットの情報を参考にするときにバージョンの違いに注意してください。

以下にsocket.io v3のtipsに関して、ドキュメント内容をざっと日本語にしています。

https://zenn.dev/dove/scraps/8112539765d869

WebSocketって結局NodeJSのほうが相性がよいの?

僕自身他のフレームワークを利用したことがないので比較はできませんが、NodeJSでの導入において特に躓く部分はありませんでした。また、socket.ioを使えばサーバー側と、クライアント側が似たようなコードで実装でき、学習コストが低いです。

WebSocket通信の部分のみをNodeJSのBFFとして実装するのもありかもしれないです。

WebSocketをHTTPのPOSTの代わりとして使うのはやめたほうがいいかもしれない

ここが一番主張したい部分です。

この半年の経験から言えることは、サーバーにデータを送るPOST処理はWebSocketよりもHTTPを利用したほうがよいということでした。POSTはHTTPで、リアルタイムに共有した部分のGETのみWebSocketを使う方針です。イメージはServer-Sent Eventsですね。

理由としては以下です。

  • セキュリティ
  • テスト
  • フロントの実装が難しい

セキュリティ

WebSocketはHTTPメソッドのようなヘッダーが存在しないので、セキュリティをHTTP用のAPIとはまた別で考えないといけません。

テスト

POST処理は権限が関わってくることも多く特にテストしたい部分です。WebSocketでPOST処理を書いてしまうと、WebSocketでテストしないといけません。WebSocketでのテストはHTTPほど一般的でなく、サンプルを探すのにも一苦労します。(POST処理だけでもHTTP化すれば既存のテスト環境リソースが使えたのに。)

フロントの実装が難しい

socket.emit()とsocket.on()が別々の場所に書かれることで、リクエスト失敗の例外処理とかバラバラになります。

リクエストの識別が難しいです。socket.emitで送ったリクエストをsocket.onで成功レスポンスを期待するとします。しかし、socket.onで受け取ったリクエストは、それが先程送ったsocket.
emitの結果かどうかはわかりません。そのため、bodyに識別idをつける送信し、受け取るときは先程のidと一致している確かめる必要があります。HTTPであれば、リクエストは送信と受信が一つであるため、そのような識別idはそもそも必要ありませんでした。(socket.ioにはHTTPのような行って帰ってくる挙動をさせるオプションはあります。)

上記の状況を僕が実際に落ちった例を紹介します。例えば、テキストをローカルストレージに保存しておいて、リクエスト成功時に削除、失敗時には削除しないという挙動を考えます。先程説明したとおりsocket.emitで送信してsocket.onで成功レスポンスを受信しても、これがはたして先程socket.emitで送ったものなのかわかりません。ローカルストレージのテキストを削除してもよいのか判断ができませんでした。

フロントでWebSocketを導入すると、うまく実装しなければ1つのWebSocketクライアントを使い回すことになります。その結果コードが結合し、どんどん泥団子になっていきます。例えば、「おまけ」のReactにおけるWebSocketの導入を見ていただきたいのですが、WebSocketクライアントを導入するのも一苦労です。新規開発で、似たようなコンポーネントで似たような送信処理を書くとき、既存のWebSocketクライアントを利用したくなります。するとめでたくコードが結合し、メンテしにくいコードができあがりました(僕のレベルが低いだけかもしれませんが...)。

その点axiosのようなHTTPクライアントは導入しやすく、どこからでもリクエストを送れるので、コードが結合しにくくリファクタリングしやすいですね。

さいご

これらの難しさは特にPOSTにおいて関わってくる部分なので、最初に述べたようにPOSTだけHTTPにしておいて、リアルタイムに共有すべきデータのGETをWebSocketにすればよいという作戦ですね。

WebSocketはジュニアエンジニアにとっては扱いが難しいなってなりました。

おまけ

reactにおけるWebSocketの導入参考サイト

https://www.pluralsight.com/guides/using-web-sockets-in-your-reactredux-app

Discussion