2010年2月22日月曜日

mod_websocket for Lighty:動作概要編

やっと動作概要のエントリだ。はぁはぁ。
さて、mod_websocket for Lightyですが、以下のように動いています。


終わり。ごめんなさい、ごめんなs(ry
いや、上図だけで分かるとは思いますが、ちゃんと説明します。
まず、lighttpd.confに、以下のように記述したとします。

server.port = 8000
websocket.server = ( "/demo" => ( "host" => "192.168.0.2", port => 9000 ) )

そして、lighttpdを192.168.0.1で起動したとします。すると、lighttpdは、192.168.0.1:8000でListenします。
この後は、以下の図を用いて説明します。


  1. まず、ブラウザから、http://192.168.0.1:8000/ws_demo.htmlを要求します。
  2. すると、lighttpdは、server.document-rootで指定したフォルダから、ws_demo.htmlを配布します。
  3. 次に、ブラウザはws_demo.htmlに記述してあるjavascript、ws = new WebSocket("ws://192.168.0.1:8000/demo");を実行します。
  4. WebSocket handshakeを受け取ったlighttpd内のmod_websocketは、そのリソース"/demo"およびmandatory headersをcheckし、正しいWebSocketリクエストだと判断したら、lighttpd.confに記述されたdemo server = 192.168.0.2:9000に接続しに行きます。(不正なHandshakeの場合、現状、404 Not Foundを返します)
  5. 接続に成功すると、mod_websocketは、ブラウザに対して、WebSocket Handshake 101 Responseを返答します。(なお、失敗すると503 Service Unavailableを返しますが、このあたりは仕様に規定がまだ無かった気がします…)
  6. ブラウザからws.send("Hello");を実行した場合、'0x00' + "Hello" + '0xff'といったメッセージがmod_websocketに届きます。
  7. mod_websocketは、'0x00'と'0xff'で囲まれたメッセージを、demo serverに送りつけます。(つまり、'0x00'/'0xff'を削除します)
  8. demo serverは、受け取ったメッセージを操作して、例えば、"How r U?"と送りかえします。('0x00'および'0xff'などは付けません)
  9. 送り返されたメッセージは、mod_websocketにより、'0x00' + "How r U?" + '0xff'とWebSocket Frame Formatに変換され、ブラウザまで配信されます。
  • 利点
  1. backendサーバと、WebSocketの動作が完全に切り離されているため、lighttpdを落とすこと無く、WebSocketで提供するサービスだけ再起動が可能となります。
  2. backendサーバは、通常のTCP Serverならば、どんな言語で作成されてもかまいません。現状githubには、C言語で作成したecho severとchat server、rubyで作成したecho severのサンプルを入れてあります。
  3. また、backendサーバはIP Reachableな環境で起動していればよいので、WebSocketで提供するサービスのscalabilityが向上します。(一概には言えませんが)
  • 難点
  1. mod_websocketとbackendサーバの間の通信は、全くメッセージフォーマットの無いTCP connectionが張られます。つまり、メッセージの開始と終了の判断がつかないこととなります。
    その為、Javascript内部で送信するメッセージと、backendサーバの間でちゃんとメッセージフォーマットを決めないと、メッセージのハンドルに困ります。(githubのhtml/ws_echo_ruby.htmlと、src/sample/ws_echo.rbを見ていただくと理由が分かるかと思います)
  2. 多分…ですが、この設計ではSub Protocolを扱うことが出来ません。もし、Sub Protocolが、「同一WebSocketリソースに対する引き数(argument)」という仕様だった場合、mod_websocketとbackendサーバの間で引き数を受け渡すプロトコルが必要となってしまいます。
    まだSub Protocolの仕様がはっきりしないため放置していますが、もし、上述のような仕様となった場合、mod_websocketではリソースを分けてご利用ください、と言うexcuseが発生すると思います。
# 2について、わかりづらいかと思いましたので、少し追記を。
まず、Sub Protocolについては、
http://jsgt.org/mt/archives/01/002777.html
@toshirotさんが、ちょうど翻訳してくださっていますのでご参照を。

例えば。
'/chat'と言うWebSocketリソースがあるとします。そのリソースでは、chatサービスを提供しているのですが、実はサーバ1つで、chat_aサービスとchat_bサービスといった2つのサービスを提供できる。つまり、
switch (sub_proto) {
case 'chat_a':
    ....
    break;
case 'chat_b':
    ....
    break;
default:
    ....
    break;
}
こんなことが出来るサービスなんです、と。

この場合、pywebsocketなど、Service用のApplicationがmoduleと一体になっているような設計では、WebSocket-Protocolヘッダの中身を見ることによって、Applicationを分岐させることができます。
しかし、Reverse Proxyっぽい設計では、Sub ProtocolをApplicationに通知できません。(もちろん、通信手順を決めれば出来ますけど)
その為、以下の様にリソースを分けて対処するしかないかなぁ、と思っています。(良い解決手段があれば教えてください。(^-^;)
<例>
WebSocketリソースが'/chat'かつ、WebSocket-Protoが'chat_a'の場合は、192.168.0.2:9000へ。
WebSocketリソースが'/chat'かつ、WebSocket-Protoが'chat_b'の場合は、192.168.0.2:9001へ。
こうするしかないかな、と。

以上、mod_websocketの動作概要でした。

とりあえず、あとはlighttpd.confのparse -> hctxに変数作ってallowed origine, sub-protoを叩き込む(そして、handshake時に参照する)、と言うTODOが残っているのですが、ちょーめんどくさいです…。
そんなに難しくはないと思いますので、誰か作って!…誰も居なければ頑張りますけど(T_T。

さて、今後は更新が滞ると思いますが、HTML5を推進されていらっしゃる皆様、頑張ってください!心より、応援しております。

ではでは。

0 件のコメント:

コメントを投稿