2010年01月19日

websocket pipelineのサンプルが意味するところ



僕が先日書いたエントリー websocketでpipelineをすると、感動的に早い!! ですが、高橋登史朗さんのご厚意でネットに公開していただきました。
ありがとうございます>高橋さんm(__)m

さて、こちらのサンプルサイトですが、"via websocket"と"via xhr"とで比較すると、10倍以上のレスポンスの違いを体験される方が多いと思います(多分)。ちなみに、僕の自宅環境(東京)では、今測ったところwebsocketが150msecぐらいで、xhrが3秒ぐらいでした。おそらく、東京から離れるにしたがって、その差は大きくなると思います。(websocketの結果は殆ど変わらず、xhrの結果だけが増大していくはず)

こちらのデモでは、文章を文で区切り("。"や"."で split)、文単位で、解析リクエストをサーバーに要求しています。なので、デフォルトの"坊ちゃん"の場合は133回解析リクエストを投げています。(なんで、そんなことをしているかについては後述)

ここで"via xhr"では、いわゆる"request & response"のマナーで要求を送信しています。従って、最初の文の解析要求を投げ、結果が返ってきたら次の文の解析要求を投げる・・・ということを繰り替えしています。ここで、「結果が返ってくる」間はただ単に待っているだけの時間であり、単にトータルの待ち時間を伸ばしているだけです。そして、その待ち時間は、サーバーとPCとの距離が伸びれば伸びるほど増加します。
(2010.1.21 01:00追記:レスポンスを待たずに、別にセッションを生成してパラレルに次の要求を投げることも可能なのですが、(1) ブラウザは同一サーバーに対するセッション数に制限がある(2〜6ぐらい)(2) パラで投げても、大して変わらない(ローカル環境では)(3) ちょっと動作が怪しかった ことから、一つのコネクションでレスポンスを待ってから次のリクエストを投げるという仕様にしています)

例えば、僕のPCから高橋さんのサイト(bloga.jp)にpingを送信するとRTT(round-trip-time)は大体15msecぐらいです。これが1回あたりのリクエストの待ち時間になります。ここで、上述のように約100回リクエストを投げた場合,トータルの待ち時間は大体1.5秒・・・(1) と計算されます。それに、サーバーでの処理時間を足したものがトータルの処理時間になりまが、サーバーでの処理時間は距離には殆ど依存しません。例えば僕の開発環境では1.5秒・・・(2) ぐらいでしたので、(1)+(2)がトータルの処理時間で3秒と計算されます。概ね、高橋さんのサイトで計測したのと同様の値になりました(^-^)

一方、websocket pipelineではレスポンスが返ってくるのを待つこと無く、次の文の解析要求をサーバーに送信します。従って,xhrのようなレスポンス待ち時間は発生しませんので,local環境と同様のレスポンスタイムが保持されます(正確には、インターネットというbest effortの環境で”保持される”という言葉は適切ではありません)。従って,サーバーとの距離が伸びるにつれ、websocket pipelineとxhrとのレスポンスタイムの差は肥大していきます。あくまで想像ですが、札幌や沖縄から上述のサイトにアクセスすると、その差は歴然となるでしょうし、地球の裏側のブラジルからアクセスすると、かなりの差になると予想できます。(実際の計測タイムコメント頂けると嬉しいです)

ここで、「そんなに差が出るのは、文章を細切れにしているからじゃない?だったら、xhrでは文を区切らなければいいじゃん」という疑問を持たれた方は多いかと思います。この疑問は、非常に的を得ており、実際その通りです。

ただし、この方式では以下のような問題が想定されます。

1.サーバーでのメモリの浪費

サーバーでは、リクエストパラメーターに比例したメモリを消費します。従って、クライアントから送信する文書が長ければ長いほどサーバーのメモリを大量に消費することになりますので、ある程度のトラフィックをさばくサイトでは、その消費量が問題となります。

2.リクエストに不正なキャラクターが含まれている時

実装依存ではありますが、リクエストの文字列に不正なキャラクターが存在した場合、文書全体をエラーとしてしまうため、全く解析されないという事態が起こり得ます。細切れであれば、該当する文だけがエラーとなり、それ以外の文章は、正常に解析されますので、エラーのハンドリングが非常にシンプルになります。

3.Head Of Line Blocking

非常に長い文章を解析・応答している間、他の要求に対し処理できない状態になります。例えば、チャットで、あるメンバーが非常に長いメッセージを要求している間、他のメンバーが緊急性の高いメッセージを送信しても、サーバーは他のメンバーにそのメッセージを送信することが難しくなります(長文メッセージの送受信完了まで、他のメッセージをハンドリングするのが難しい)。一方、細切れ(文単位)で処理すれば、緊急メッセージを即座に他のメンバーに伝えることが容易となります。

従って,ある程度の規模のシステムで、柔軟かつシンプルな系を構築し、サービスとしてのfairnessを実現する際には、メッセージはある程度細切れにしたほうが良いと考えられます。

人気ブログランキングへ
kotesaki at 02:39│Comments(31)TrackBack(2)clip!websockets | html5

トラックバックURL

この記事へのトラックバック

1. websocketsのpipeline  [ kumama ]   2010年02月04日 21:58
またもEmerge Technologyからのネタ。 この、websocket pipelineのサンプルが意味するところら辺の話。 # ちなみにHTML5のドラフト上、「websocket」ではなく「websockets」が正解・・・だったと思うw websocketsの利用シーンとしては現状httpでは出来ないサーバ側からの通知...
2. websocketsのpipeline  [ kumama ]   2010年02月15日 23:55
またもEmerge Technologyからのネタ。 この、websocket pipelineのサンプルが意味するところら辺の話。 # ちなみにHTML5のドラフト上、「websocket」ではなく「websockets」が正解・・・だったと思うw websocketsの利用シーンとしては現状httpでは出来ないサーバ側からの通知...

この記事へのコメント

1. Posted by 高橋登史朗   2010年01月19日 15:14
茨城--シアトル間です

===============================================
http://bloga.jp/ws/jq/wakachi/mecab/wakachi.html
case websocket (pipeline) 606 msec
case XML HTTP request (parallel Ajax) 31863 msec
# 52倍

http://bloga.jp/ws/jq/wakachi/mecab/ruby.html
case websocket (pipeline) 1000 msec
case XML HTTP request (parallel Ajax) 30951 msec
# 30倍

>tracert bloga.jp
ホップ数13

>ping bloga.jp

bloga.jp [202.215.119.36]に ping を送信しています 32 バイトのデータ:
202.215.119.36 からの応答: バイト数 =32 時間 =190ms TTL=52
202.215.119.36 からの応答: バイト数 =32 時間 =192ms TTL=52
202.215.119.36 からの応答: バイト数 =32 時間 =192ms TTL=52
202.215.119.36 からの応答: バイト数 =32 時間 =191ms TTL=52

202.215.119.36 の ping 統計:
パケット数: 送信 = 4、受信 = 4、損失 = 0 (0% の損失)、
ラウンド トリップの概算時間 (ミリ秒):
最小 = 190ms、最大 = 192ms、平均 = 191ms
--
2. Posted by 高橋登史朗   2010年01月19日 15:37
予想通り、xhrの遅延量が大きく増えましたね。
wsの方は、たとえば、3つのリソースに分けて処理してもゆとりは十分あると思うので、まだまだ早くできそうな気もします。
3. Posted by 高橋登史朗   2010年01月19日 15:43
p.s. それにしても30秒では一般のWebアプリではあまり使い物にならないですけどf^^;、1秒なら使える範囲だと思います。この差は 0 と 1 くらい大きい気がします。
4. Posted by 高橋登史朗   2010年01月19日 15:53
近いけどISDNなので時間がかかるケースのテスト


茨城守谷--牛久(ISDN)

===============================================
http://bloga.jp/ws/jq/wakachi/mecab/wakachi.html
case websocket (pipeline) 10160 msec
case XML HTTP request (parallel Ajax) 50467 msec
# 5倍

>tracert bloga.jp
ホップ数8

>ping bloga.jp

Pinging bloga.jp [202.215.119.36] with 32 bytes of data:

Reply from 202.215.119.36: bytes=32 time=419ms TTL=58
Reply from 202.215.119.36: bytes=32 time=582ms TTL=58
Reply from 202.215.119.36: bytes=32 time=555ms TTL=58
Reply from 202.215.119.36: bytes=32 time=520ms TTL=58

Ping statistics for 202.215.119.36:
Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 419ms, Maximum = 582ms, Average = 519ms
5. Posted by 高橋登史朗   2010年01月19日 15:56
牛久(ISDN)はマシンもCeleron2.66Ghz の非力PCですので、描画速度の遅さも加味されてますf^^;
6. Posted by edvakf   2010年01月19日 21:15
ほぼ地球の裏側 (カナダ、モントリオール) です。

分かち書き
WebSocket 1321 msec
XHR 38679 msec

ルビ
WebSocket 1554 msec
XHR 38884 msec

でした。ブラウザは Chromium です。どちらも、XHR で60〜70あたりからガクンとスピードが落ちました。

しかし、本当に感動的に速いですね!
7. Posted by 高橋登史朗   2010年01月19日 22:03
モントリオールが、シアトルに少し足したぐらいの値というのは偶然かもしれませんが面白いです(^^
8. Posted by makoto   2010年01月20日 20:31
こんにちは

非常に興味深い情報をありがとうございます。

ただ1点質問させていただきたいことがあります。

なぜxhrでのみレスポンスを待たなくてはいけないのでしょうか?
xhrのほうでも細切れにした文をレスポンスをまたずに次のリクエストとしてわたすのは不可能なのでしょうか?

両方とも同じロジックでテストしないとベンチマークの意味合いが薄れてしまう気がするのですが、もし見落としている点があればおしえていただけるとありがたいです。
9. Posted by 高橋登史朗   2010年01月20日 22:09
あれ?ソースでは受信を待つ仕組みではなくて、50個投げて50個投げて、のこり30個っていうだけじゃない?

誤解産みそうだから試しにbulk = 150にしてみたらもっと遅くなりました。

で、試しにbulkとencodeURIComponentもやめちゃったのに取り替えてみました。
case websocket (pipeline) 195 msec
case XML HTTP request (parallel Ajax) 2498 msec133

どうですか?
10. Posted by 高橋登史朗   2010年01月20日 22:16
onSuccessでthat._send()は最初からコメントアウトされてましたので、、、

したがって、推測ですが、小松さんの文中の「結果が返ってきたら次の文の解析要求を投げる・・・ということを繰り替えしています。」という付近は、古いテストの話で、今回のテストでは少し違うのかな?と思います。
11. Posted by こまつ   2010年01月21日 00:07
makotoさんありがとうございます!!

httpでもpipelineというテクニックは存在します。具体的には,複数のGETメソッドを一度に送信することで実現します。(下記のURIがわかりやすい)
http://blog.livedoor.jp/dankogai/archives/50719947.html

ご指摘のとおりで,僕も最初はhttpとwebsocketでフェアに比較しようとしました。

しかし調べてみると、ブラウザでこのテクを使うのは困難で、例えば chrome でこのテクを使うための設定変更を見つけることはできませんでした。firefoxはabout:configで設定変更可能(結構きつめの警告が出ます)なのですが、このテクを使うためのjavascript(api)を僕は見つけることができなかったため、ギブアップしたというのが実状です。
12. Posted by こまつ   2010年01月21日 00:16
高橋さん、コメントありがとうございます!!

おそらく、古いバージョンを使われていると思います。最新のソースでは、高橋さんが修正されたのと同様の改修がされています。すみません、混乱させてしまって。。。
(僕の環境で、ちょっと動作が怪しかったので、パラは止めています)

ちなみに、古いバージョンはパラレルでajaxを投げているので,最新バージョンより若干早いと思います。ただし、ajaxでは同一サーバーにパラで送信できるコネクション数が制限されている(2〜6ぐらい。実装依存)ため、bulk=50としても、たかだか数個しかパラで動きません。なので、コネクション1個と比べても、大差は無いと思います。(パラで2個ならパフォーマンス2倍
となりそうなものですが、僕の環境ではそこまで変わりませんでした。。。なぜかな?)
13. Posted by 高橋登史朗   2010年01月21日 00:28
xhrは一応、asyncなので、コメントアウトされていた古いコードのようにsendをonSuccessに縛り付けなければ、着信を待つことなく、どんどん送出されていきます。

ただし、普通は、ブラウザ側からいわゆるパイプラインで一気に送出はできませんので、結局、結構時間がかかってしまう、という感じかなぁ、、、そのうえヘッダのボリュームが大きいという。
14. Posted by 高橋登史朗   2010年01月21日 00:32
というわけで、今となっては、上の「結果が返ってきたら」と言う部分は修正しておいた方が、意図しない誤解を避けられて良いかも?
15. Posted by 高橋登史朗   2010年01月21日 00:33
P.S. bloba.jp --> bloga.jp です(^^)
16. Posted by こまつ   2010年01月21日 00:50
>14 onSuccessに縛り付けないと別のコネクションを新たに生成することになるので、”同一コネクションに対しては”レスポンスを待たないとリクエストを返せないという意味で使っています。意味合いが微妙なので、表現が難しいところですね。

>15 うっっっ、、、すみません、直します。。。
17. Posted by こまつ   2010年01月21日 01:01
>14 追記で補足しておきました(^-^)
18. Posted by 高橋登史朗   2010年01月21日 02:22
私が勝手にこのスクリプトのXHRのbulkをさっき修正してしまったので、もしかすると、このXHRの動作について小松さんにも誤解が起きているかもしれません。

今、パケット見ながらXHR版を実行してみたのですが、送出送出送出受信受信受信送出受信送出受信...と言う具合で、今は普通のXHRとして、受信を待たずに、非同期に 送受信が行われています。

もしかして、私は何か勘違いしているかも??
19. Posted by 高橋登史朗   2010年01月21日 02:38
修正後のシアトルデータ届きました

========================================
http://bloga.jp/ws/jq/wakachi/mecab/wakachi.html

case websocket (pipeline) 618 msec
case XML HTTP request (parallel Ajax) 34225 msec


しかし、このテストは、パケットキャプチャの描画が追いつかない(笑)。
20. Posted by makoto   2010年01月21日 02:51
こまつさん。高橋さん。情報ありがとうございます。大変ためになりました。
Chromeのpipelineはまだ実装されてないとwikipediaにはあるようです。
http://en.wikipedia.org/wiki/HTTP_pipelining#Implementation_in_web_browsers

pipeline はひとつのソケットにたくさんのリクエストを流し込むアイデアのようですが、単純に1ソケット1リクエストとしてたくさんのソケットをあけるのは無理でしょうか?ブラウザが一度にどれだけのソケットをオープンできるか調べていないので、ちょっと微妙ですが。
21. Posted by 高橋登史朗   2010年01月21日 03:07
シアトル結果2 です
========================================
http://bloga.jp/ws/jq/wakachi/mecab/ruby.html

case websocket (pipeline) 769 msec
case XML HTTP request (parallel Ajax) 30507 msec
22. Posted by 高橋登史朗   2010年01月21日 03:41
複数ソケット接続できますよ。たとえば、
http://bloga.jp/ws/jq/conn/wsdemo-wrcm.htm
これは、2つのリソースにつないでいます。

Maxはまだ調べてませんが、

これは、同一ページで、4つのソケットを開いてみています。
http://bloga.jp/ws/jq/conn/m3.htm
23. Posted by Norio Kobota(@nori0428)   2010年01月21日 10:29
古い記事にコメントごめんなさい。
1.xhrにてasyncでsend→onSuccessで非同期に処理
2.wsでsend→onmessageで非同期に処理

この違いについては、高橋さんの13番目のコメント
> 普通は、ブラウザ側からいわゆるパイプラインで一気に送出はできません
とあるように、xhrでは大体5〜6session分のmessageしか送信出来ないのに対し、wsではいくつでもメッセージをsend出来る、という違いがある。
(つまり、wsではserver側でより多くpipeline処理出来る)

また、xhrではsendする度にhttp sessionが張られるのに対し、wsでは1回のhttp sessionしか張られない。
(つまり、wsではSYN - SYN ACK - ACKが1度だけ)

以上から、遅延に関しては、

1.ネットワーク遅延: sessionを貼り直すか否か。
2.処理遅延: server側でどれだけpipeline処理出来るか。

の2つが存在し、(http sessionが張られる度にかかるhttpdの処理遅延、というのも考えないといけないのですが)

個人的にはwsのメリットは、どちらかというとネットワーク遅延の改善の方が大きい気がしているのですが、実際にはいかがでしたでしょうか?
24. Posted by こまつ   2010年01月21日 10:51
>20, 22
RFC2616(HTTP 1.1)では、同時接続コネクション数は2にすべきであると記述しています。

http://www.ietf.org/rfc/rfc2616.txt P.47より
---
A single-user client SHOULD NOT maintain more than 2 connections with any server or proxy.
---
ただし、ここのlimitationについてはまだIETFで議論されていて、実際にはブラウザの実装依存になっているようです(たぶん、多くても6ぐらい?)

明確に言えることは、コネクションリミットを設けないと、簡単にサービスをアクセス不能にできちゃいますので、せいぜい数コネクションぐらいに抑えるべきだと思います。(同時に150コネクションとか貼ると、apacheデフォルトであれば、ほかのユーザーがアクセスできなくなる)

テストは、まだしていませんがwsでも、ここの最大コネクション数は制限されていると思います。wsはhttpの拡張ですので。

従って、コネクション数を抑えつつ、簡単にpipeline処理が行えるwsはすばらしいなぁというのが小松の所感で。:-)
25. Posted by こまつ   2010年01月21日 11:00
> 23

1.(3 way handshake)については、HTTP/1.1のpersistent connectionにより、省略可能です。
 #ただし、同時運用だとpersistent-connectionを外すことに
 #なるので今のテスト環境だと3 wayも効いているのは事実です。

問題なのは、httpでは(http pipelineを使わない限り)一つのコネクションあたり同時に一つのリクエストーレスポンスしか扱えないため、どうしてもレスポンスパケットが返ってこないと、次のリクエストが送信できないところにあります。従って、1. + 2. で「ws では pipeline処理ができるので、ネットワーク遅延の影響をうけづらい」と考えるのが妥当かなぁと考えています。
26. Posted by Rs Gold   2013年02月17日 17:55
問題なのは、httpでは(http pipelineを使わない限り)一つのコネクションあたり同時に一つのリクエストーレスポンスしか扱えないため、どうしてもレスポンスパケットが返ってこないと、次のリクエストが送信できないところにあります。従って、1. + 2. で「ws では pipeline処理ができるので、ネットワーク遅延の影響をうけづらい」と考えるのが妥当かなぁと考えています。
27. Posted by Diablo 3 Gold kaufen   2013年03月22日 22:31
クエストの文字列に不正なキャラクターが存在し
28. Posted by GW2 gold   2013年03月22日 23:05
ほぼ地球の裏側 (カナダ、モントリオール) です。
29. Posted by Guild wars 2 Gold   2013年04月11日 21:16
#なるので今のテスト環境だと3 wayも効いているのは事実です。
30. Posted by GW2 Gold   2013年05月08日 20:47
この記事では、私は他の人と共有転載したい、非常に良いです!
31. Posted by Air Jordan 6 Champagne   2014年06月27日 20:56
こてさきAjax:websocket pipelineのサンプルが意味するところ - livedoor Blog(ブログ)

この記事にコメントする

名前:
URL:
  情報を記憶: 評価: 顔