High (Availability|Performance) WebSocket for Perl Real-Time Application

B5582ce2d9959dfcff0384a07003e188?s=47 mackee
March 03, 2018

High (Availability|Performance) WebSocket for Perl Real-Time Application

このスライドはYAPC::Okinawa ONNASON 2018で行ったトークです。

トーク概要 http://yapcjapan.org/2018okinawa/talks.html#/detail/6

B5582ce2d9959dfcff0384a07003e188?s=128

mackee

March 03, 2018
Tweet

Transcript

  1. 1.

    High (Availability|Performance) High (Availability|Performance) WebSocket for Perl Real-Time WebSocket for

    Perl Real-Time Application Application macopy a.k.a @mackee_w YAPC::Okinawa 2018 ONNASON
  2. 7.

    !!! CAUTION!!! このトークでの High Availability High Availability 高可用性 これほどコンテキストによって変わる高ナントカはあ まりないと思う

    SPOF がない 片方落ちたときにクライアントの再接続でもう片方でサービス継 続が可能 リトライ可能
  3. 9.

    !!! CAUTION!!! このトークでの High Performance High Performance WebSocket 同時接続数 10k

    ~ 100k 上記数字は実際の本番での運用での数字 横に並べられる ボトルネックになることを避ける設計 もっといけるとは思う
  4. 39.

    WebSocket WebSocket メリット: 現代においては広く使われている, 標準化もされてい る, 双方向 デメリット: HTTPS じゃないWebSocket

    だとフォワードプロキシが ある環境で使えないことがある, デバッグ方法が通常のHTTP の作 法と違う
  5. 45.

    時代が移ろいでいきPerl 界では mod_perl perl インタプリタの起動とモジュールの読み込みは予めしてお く FastCGI Apache からの解脱, lighttpd

    ってのがあってだな PSGI もうperl がHTTP をしゃべっちゃう Perl ウェブ開発の中世~CGI と Plack の間~ - YAPC::Kansai 2017
  6. 56.

    Q. 何故、同期的Prefork モデル Q. 何故、同期的Prefork モデル が成り立っていたか が成り立っていたか A. 普通のHTTP

    通信は刹那的な A. 普通のHTTP 通信は刹那的な プロトコルだから プロトコルだから
  7. 57.

    今までのHTTP では1 つのリク 今までのHTTP では1 つのリク エストが1 つのプロセスを専 エストが1 つのプロセスを専

    有する時間が短い。だからこ 有する時間が短い。だからこ れでやってこれた れでやってこれた
  8. 59.

    C10K の話をしています C10K の話をしています C10K = Connection 10,000 Problem サーバリソースはあるのにアーキテクチャの問題でリクエストを

    さばくことが出来ない問題 本来はHTTP Keep-Alive の文脈からも論ぜられますが、Keep-Alive はnginx とかが維持してくれるし、刹那的HTTP に 変換可能なのでアプリケーションの変更はいらない
  9. 67.

    別の解法 => サーバプッシュだ 別の解法 => サーバプッシュだ け別にやる け別にやる Node.js でSocket.IO

    しゃべってRedis pub/sub でWebApp からつなげ る Go で似たようなことをgRPC/SSE でやる ActionCable Ruby on Rails でWebSocket をしゃべる機能 kuiperbelt <- 本日はコレの話 openfresh/plasma
  10. 70.
  11. 77.

    認証用のHTTP サーバを作る my $app = sub { my $env =

    shift; my $req = Plack::Request->new($env); # some authentication processes my $endpoint = $req->header("X-Kuiperbelt-Endpoint"); my $session_id = generate_uuid(); $KVS->set("$user_id:kuiperbelt_session", { session => $session_id, endpoint => $endpoint }); return [200, [ "Content-Type" => "application/json", "X-Kuiperbelt-Session" => $session_id, ], ['{"message": "Hello WebSocket World!"}'],
  12. 80.

    Perl からメッセージを送る なんでもいいけれどHTTP でAPI 叩けば良いです。Body の形式は問い ません。 # $target_user_id 対

    $message 送 my $furl = Furl->new; my $session = $KVS->get("$target_user_id:kuiperbelt_session"); $furl->post( $session->{endpoint} . "/send", [ "X-Kuiperbelt-Session" => $session->{session_id} ], $message, );
  13. 81.

    もともとの動機 もともとの動機 Perl5 でWebSocket とかSSE を喋りたかったが、 AnyEvent とかMojo 使えばPerl5 でWebSocket

    使えるやん? あ、そのせいでイベント駆動プログラミングをしないといけなく なった…… DB のトランザクションかけようとしたら、混ざるんじゃないの これ?
  14. 85.

    仕組み 仕組み 1. HTTP API でWebApp からメッセージを受け取る 2. AnyEvent::APNS を使ってApple

    のサーバにつながっているソケッ トにメッセージと送り先トークンを書き込む 3. プッシュ通知が届く HTTP でiOS のpush 通知を送れる未来がやってきた(APNS::Agent の紹 介)
  15. 86.

    偉大なところ 偉大なところ やることに合わせたアーキテクチャを使い分けれるようにデーモ ン化して切り出した 一つのことをうまくやる 土管に徹する UNIX 哲学 HTTP API

    というどの言語でも持っているようなインターフェイス で触れる 現在では というデーモンに思想は引き継がれてAndroid にも通知が送れるようになっています Gunfish
  16. 90.

    一般的なWebSocket の接続を 一般的なWebSocket の接続を 考える 考える 1. クライアントから普通のHTTP リクエストが来る 2.

    サーバは繋いで良いクライアントだったらUpgrade する 3. WebSocket 接続を開始する 4. 適宜送ったり、受け取ったりする 5. 切りたいときに切る
  17. 95.

    Body はApp のもの Body はApp のもの WebApp がメッセージを投げるときに叩くHTTP API のリクエスト

    のBody の内容をそのままWebSocket に書き込んでいる を指定すればバイナリも突っ込める msgpack を使っているプロジェクトもあります
  18. 96.

    Body はApp のもの Body はApp のもの つまり何をどう送るかはWebApp に委ねられている => 土管に徹す

    る思想から なので送信オプションとかはHTTP ヘッダを使うようにしている
  19. 111.

    先行事例 先行事例 Node.js やActionCable など Redis pub/sub を用いる( メッセージキュー) WebSocket

    の接続を持つサーバが部屋ごとに作られたtopic を subscribe しておいて、WebApp 側が部屋のtopic に対してメッセ ージをpublish する Redis さえスケールさせれば、そうRedis さえ落ちなければ……
  20. 119.

    Q. 結局KVS 使っているので Q. 結局KVS 使っているので は??? は??? A. ユーザID

    と接続識別子の変 A. ユーザID と接続識別子の変 換にKVS とか使うでしょ 換にKVS とか使うでしょ
  21. 120.

    Q. 識別子= ユーザID とかにす Q. 識別子= ユーザID とかにす ればKVS 必要じゃなくない?

    ればKVS 必要じゃなくない? A. 1 人のユーザが複数のWS 接 A. 1 人のユーザが複数のWS 接 続持つケースがあって 続持つケースがあって
  22. 128.

    結構古いバージョンの結果なので今とは性能が違う場合があります サーバは2 コア2 台です clients event CPU Mem(RSS) msgs/sec 1000

    1000 13% 68MB 2666 5000 2000 24% 366MB 5388 5000 4000 49% 355MB 10916 10000 10000 120% 688MB 26666 10000 1666 22% 737MB 4466
  23. 137.

    ログレベル ログレベル Perl だと がよく使われている CRITICAL, WARN, INFO, DEBUG に出力するログを分類する

    kuiperbelt はDEBUG にガンガンログを出している => WebSocket のデバッグがめんどくさいので開発中に躓いて出したログはだ いたい残している その代わりに運用中にはDEBUG やINFO のログは切っている Log::Minimal
  24. 146.

    Docker 対応 Docker 対応 カヤックでもECS にWebApp を載っけるのを進めています WebApp をコンテナに押し込んだら、kuiperbelt もコンテナに押し

    込みたい が、kuiperbelt を複数台構成にしたときは個別のコンテナにつな がるホストとポートをkuiperbelt 自身が知る必要がある
  25. 153.

    未来の話: ekbo-router 未来の話: ekbo-router prefix base routing kuperbelt インスタンスと識別子のprefix を対応させる

    ここは分散KVS が必要...... WebApp はどこに接続があるかを気にせずにローカルのekbo- router に投げれば良い