何故だか昨日のエントリが意外なほど読まれている。(^^;;;
Twitterでの言及も過去最大級かも。特に、
サーバーとの通信はAjax併用でも良いんですが、多分全部WebSocketでやる方がシンプルだし速度面やセキュリティ面でも優位があります。
この文章に引っ掛かった人が多いようで、これについてもうちょっと掘り下げてみます。
★ Ajaxの代替としてのWebSocket
そもそもの話として、素のWebSocketではリクエスト(この場合クライアント→サーバのメッセージの意)とレスポンス(サーバ→クライアント)を対応付けることができません。これを実現するためには自力でサーバ/クライアントの双方にメッセージをハンドリングするための仕組みを実装しならず、それは結構めんどうな作業です。
今回、その部分をフレームワーク化しようとしているので、それが実現できた場合のメリットを考えてみます。
ざっと思いつくのは以下です。
- KeepAliveかつ、httpヘッダのパースも不要になるので速い
- 特に断続的なリクエストが大量にある場合大きな優位となる(実験結果)
- 冗長構成でサーバが複数台ある場合も単一クライアントからのリクエストは常に同一サーバに送信される
- 単一接続からのリクエストは常に同一クライアントからのリクエストであることが保証される
速度メリットは実はおまけみたいなもので、重要なのは最後の2つです。これ、言い換えればWebSocketをAjaxの代替として使うことでステートフルな通信が可能になるということなんです。
既存のWebサーバーではリクエストルーティング機構のインスタンスはサーバー単位で1つなので、リクエスト処理の流れは
- リクエスト受信
- Cookieを使用してユーザーを同定
- 必要ならMemcache等からステート情報を取得
- リクエストを処理
のようになりますが、WebSocketの場合接続毎にリクエストルーティングのインスタンスを作ることができるので、2、3の手順がまるまる不要になります。
リクエスト処理のパフォーマンスを出すためになんらかのキャッシュを行う場合もオンメモリだけで間に合いますし、Cookieやhiddenパラメータを使って情報を引き回すことも不要になります。
# ただし、意図しない切断はありえるので長時間ステートを保持することや常にステートが維持されていることを前提としたプログラミングをすることはお勧めしません。この辺のベストプラクティスはまだ確立されていないので当面は自分のバランス感覚だけが頼りになります。(^^;
★ WebSocketとセキュリティ
前述の通り、WebSocketでは単一接続からのリクエストは同一クライアントからのものであることが保証されます。
近年のJavaScriptプログラミングでは処理全体をdocument#readyのクロージャで括るのが一般的ですが、その場合仮にXSSの脆弱性があったとしても外部から接続済みのWebSocketインスタンスにアクセスすることは不可能です。
つまり、最初の接続時にクライアントの確認をしっかりと行っておけば以降のリクエストは攻撃ではないと見なすことができます。なのでCSRFのチェック等は不要です。
問題は接続時の確認を、どのように行うかですがこれはこの資料が参考になります。
http://www.slideshare.net/muneakinishimura/webhtml5-31749532
ほとんどの場合はくログイン認証自体は従来のhttp(s)接続で行ってその後はCookie併用でクライアントを同定することになると思います。
http接続とws接続が同じオリジンであればこれで十分なはずです。
次節で紹介するWebSocketにはSame Origin Policyがないことを利用したスケールアウトを行う場合はもう少し考慮が必要になります。
★ WebSocketとスケール
とりあえずルームモデルでも単純な水平スケールが可能であることは確認済みです。
ただ、この方法には効率の悪い部分があります。
というのは、サーバ100台に対して100人のユーザが1つのルームに入室した場合、最悪100人全員が異なるサーバに接続する可能性があるからです。
ユーザがばらけるとバックエンドのRedisの負荷が上がるので同じルームのユーザは1台とは言わないまでも極力小数のサーバに集中してほしいのです。
HerokuやELBなどフロントにロードバランサがいる場合、こうしたリクエストを適切にふりわけることはできません。(パス毎にリクエストを振り分けるロードバランサがあれば、可能ですがあまり筋の良い解決方法とは思えません。)
が、実はもっと簡単な解決方法があります。
WebSocketにはSame Origin Policyがないので単純にサーバを分けてしまえば良いのです。
ページをリクエストするホストが「http://www.quizar.info/...」だったとしても、その中でws接続するホストは「ws://room1.quizar.info/...」であっても良いわけですね。
この場合、接続時のクライアント認証をどうするかが課題になりますが、Cookieのドメインを明示するとかバックエンドキャッシュを使うとかいくつか方法はあると思います。(必要に迫られたらちゃんと考えます。(^^;)
このアーキテクチャを採用する場合、同一のコードベースから簡単に複数のアプリケーションを作成できるHerokuは最強のWebSocketプラットフォームだと思いますが、実際にやって良いかどうかはまた別の話です。(少なくとも課金を減らす目的でやるのはアウトだと思います。実際に必要になったら問い合わせますが、パッケージ買いでその範囲で行う分には問題ないだろうと思います。)
★ 課題
最大の課題はやはり切断時の対処です。HerokuもELBも無通信状態が長く続くと切断されますし、スマホの場合スリープやブラウザから別アプリに切り替えることも当たり前の操作なのでそれも考慮しなければなりません。また、Herokuを使う場合はデイリー再起動への考慮も必要になります。
この部分もフレームワークで吸収できればと思っていますが、もうちょっと試行錯誤が必要なのでそれはまたおいおい。(^^;
□□□□
以上、現時点でのWebSocketに対する考察でした。
WebSocket。既存のhttp通信の延長で考えている人が多いと思いますが、うまく使えば劇的なパラダイムシフトが発生するかもしれません。
どこに辿りつくのかわかりませんが、もうちょっとこの道を進んでみたいと思います。(^^;;;