Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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. 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. 自己紹介 自己紹介 インターネット及び会社ではマコピーと呼ばれています 所属: 面白法人カヤック 主な職業: スマホゲームのサーバサイドエンジニア ゲーム開発, サーバ運用, ワークフロー構築

    etc...
  3.  「トーカナイザの守護霊」 興味があること: リアルタイム通信, 3D プリンタ, 自作キーボード 最近の出来事: 右肘を骨折, リハビリ中

    Twitter: @mackee_w GitHub: @mackee PAUSE: MACOPY
  4. Perl Hackers Hub にDB を使ったテストの話などを寄稿させていただ きました

  5. 予告編の内容 予告編の内容 課題と背景について説明します サーバプッシュを従来の同期型Prefork アーキテクチャで扱った 場合の問題点 何故kuiperbelt が生まれたのかの説明 スマホゲームの場合の事例です

  6. このトークの内容 このトークの内容 背景があった上でどう解決したかを解説 永続接続を扱うスケーラブルなミドルウェアの設計 そのミドルウェアを趣味で作って運用に持っていく話 Proof of Concept から Production

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

    SPOF がない 片方落ちたときにクライアントの再接続でもう片方でサービス継 続が可能 リトライ可能
  8. 人間諦めが肝心 人間諦めが肝心 信頼性が期待されているミドルウェアは、何を提供するミドルウ ェアでどこからどこまでを保証するかをはじめに決める はじめにルールを決めておけば信頼性を損ないかねない機能追加 の要望を説明して突っぱねられる 代替案は提案した方がいいけれど

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

    ~ 100k 上記数字は実際の本番での運用での数字 横に並べられる ボトルネックになることを避ける設計 もっといけるとは思う
  10. !!! CAUTION!!! このトークでの リアルタイム リアルタイム 「リアルタイムっていうからリアルタイムOS とかのリアルタイ ムかと思った」ごめんなさい違います。なのでそういうの言うだ けのブクマはしないで欲しい Web

    およびスマホゲームで使われる言葉。技術的観点からはサー バプッシュを用いた機能
  11. 第1 章 第1 章 リアルタイム通信がめんどく リアルタイム通信がめんどく さい件 さい件

  12. リアルタイム通信とは リアルタイム通信とは あるユーザの操作 時間経過やゲーム内オブジェクトの状態によって引き起こされるイ ベント が 別のユーザに即座に伝わる アーキテクチャ/ ゲームシステム のことを指す

  13. 例: ターン制カードゲーム 例: ターン制カードゲーム 相手のターンから自分のターンになる時に 場に出すカードを選択するなどしてターンを終了する

  14. 例: ターン制カードゲーム 例: ターン制カードゲーム 既にターンが終了しているかどうかを出来るだけ素早く知るに は? 今の場の状態をサーバに定期的に問い合わせる(= ポーリング) サーバから情報が降ってくるのを待つ(= サーバプッシュ)

  15. ターン終了時の通信 ターン終了時の通信

  16. ポーリング ポーリング

  17. サーバプッシュ サーバプッシュ

  18. 実現するためには 実現するためには クライアントからのリクエス クライアントからのリクエス ト無しでサーバから情報を送 ト無しでサーバから情報を送 る必要がある る必要がある => サーバプッシュ

    => サーバプッシュ
  19. 本物と擬似 本物と擬似 従来のクライアント起点のリクエストでも似たような体験を実現 出来なくはない これがポーリング クライアントが定期的にサーバに新しい情報がないかを取りに来 る。更新されていたら画面とかも更新する 疑似リアルタイムと呼ばれる

  20. ポーリング サーバプッシュ リアルタイム性 低 高 通信量 大 小 サーバリソース 大

    小 技術的飛躍度 低 高 枯れている感 高 低
  21. モバイルの場合 モバイルの場合 ポーリング サーバプッシュ 電池消費 小 大 サーバプッシュはサーバに対してクライアントが通信を繋ぎっぱ なしになるため電池を消費しがち ポーリングは通信が間欠的になるため比較的電池消費をしない

  22. メリット・デメリットあるけ メリット・デメリットあるけ れど本物が必要なアプリケー れど本物が必要なアプリケー ションは多いので本物のリア ションは多いので本物のリア ルタイム技術を採用せざるを ルタイム技術を採用せざるを 得ない今日このごろ 得ない今日このごろ

  23. 本物のリアルタイムでもラグ 本物のリアルタイムでもラグ はある はある 驚くべきことに光は1ms に300km しか進まない 驚くべきことに通信回線の資源や計算資源は有限 驚くべきことにインターネットはコンピュータのバケツリレーで 出来ている

  24. あなたはそばに相手がいると思うかもしれないが、それはインター ネットの中だけで実は地球の反対側にいるかもしれない。それがイ ンターネットの良いところ

  25. ラグを見せないゲームデザイン [CEDEC 2010] ネットゲームの裏で何が起こっているのか。ネット ワークエンジニアから見た,ゲームデザインの大原則 行動パターンが送られてこない場合でも,それ を誤魔化す小さいアクション(火を吹くなど) が用意されているとのこと

  26. Web 以外のゲームでは Web 以外のゲームでは 家庭用ゲーム機/ オンラインゲームの世界 もっぱら生TCP/ 生UDP の世界 各々がそれぞれやっている(

    シリアライズ形式とか)
  27. スマホゲーム界では スマホゲーム界では ノウハウを持ってない会社が参入してきたのでミドルウェアを提 供する会社が出てきた Photon Server / Photon Cloud (Exit

    Games, Inc) モノビットエンジン ( モノビット株式会社)
  28. 一方我々は 一方我々は 面白法人カヤックって出自がWeb の会社なんですよね 最近はゲームやったりハードウェアを設計する人もいるけれど 使われる技術スタックはWeb 由来のもの いきなり家庭用ゲーム機やPC ゲーム由来の技術を使おうとして もノウハウがない

    Photon 使っているゲームもある
  29. Web の技術 Web の技術 ニアリーイコール ブラウザで使える技術 ブラウザで使える技術

  30. HTTP の世界観 HTTP の世界観 リクエストとレスポンスは1 対1 レスポンスが生まれるにはリクエストが必要 この時点でサーバプッシュとは矛盾している

  31. HTTP は ステートレスなプロトコル ステートレスなプロトコル あるリクエスト/ レスポンスの対と、別のあるリクエスト/ レスポ ンスの対は独立している

  32. 重要な点 重要な点 一般的にWeb サービスの1 リクエストは長くても数秒以内にレスポ ンスが返ってくる このへんが後々効いてくる大事な話です

  33. HTTP の特性を踏まえた上での HTTP の特性を踏まえた上での Web でのサーバプッシュはど Web でのサーバプッシュはど うやる? うやる?

  34. ロングポーリング/Comet ロングポーリング/Comet ふつうにHTTP リクエスト投げる サーバは送りたい情報が出るまでレスポンスを送らず焦らす 送りたい情報が生まれたらレスポンスを返す クライアントは処理したらまたリクエストを投げる

  35. ロングポーリング/Comet ロングポーリング/Comet メリット: 普通のHTTP 通信が長くなっただけ デメリット: 一個情報を受け取るのに一回リクエスト送るオーバ ーヘッド, レスポンスを受け取ってから次にリクエストを送るま では情報をリアルタイムに受け取れない

  36. Sever-Sent Events(SSE) Sever-Sent Events(SSE) ふつうにリクエスト投げる サーバはコネクションを切らずにだらだら1 行ずつ書く クライアントは1 行データが送られてきたら読む、コネクシ ョンは切らない

  37. Sever-Sent Events(SSE) Sever-Sent Events(SSE) メリット: ただのHTTP のアクロバティック的使用法 デメリット: 一方通行, ライブラリ実装の少なさ

  38. WebSocket WebSocket Upgrade ヘッダが入ったリクエストを投げる サーバがWebSocket を介したいんだなって思って101 Switching Protocols を返す HTTP

    とは違う独自のWebSocket プロトコルによる通信が開始 される
  39. WebSocket WebSocket メリット: 現代においては広く使われている, 標準化もされてい る, 双方向 デメリット: HTTPS じゃないWebSocket

    だとフォワードプロキシが ある環境で使えないことがある, デバッグ方法が通常のHTTP の作 法と違う
  40. 他にも未来の技術達 他にも未来の技術達 HTTP2 Server Push html を取りに来たクライアントにこれも必要だから持ってけっ て言ってJS やら画像を送りつけることが出来るやつ ゲームなどのサーバプッシュに使えるかどうかはあまりわからない……

    この前のセッションでトークした 人達に聞いてみましょう
  41. 他にも未来の技術達 他にも未来の技術達 WebRTC ブラウザでビデオチャットとかするやつ 条件が合えばP2P 通信ができる。また画像や音声以外にもデー タも送れる。さらにUDP を選択できるのでオーバーヘッドも少 ない。ただしNAT 越え……

    gRPC みたいなWeb の技術に乗っかっているサーバプッシュの仕組みを持ったものもあるが、ブラウザでは使え ない
  42. Web サーバアーキテクチャの Web サーバアーキテクチャの 話 話 サーバプッシュを適用するにはアーキテクチャの変更が必要になる 場合がある……

  43. 太古の昔: CGI 太古の昔: CGI HTTP リクエストが飛んできたら サーバ内のコマンドを起動してリクエストを起動したプロセス に渡す プロセスが吐いた出力をレスポンスとして返す

  44. 太古の昔: CGI 太古の昔: CGI メリット: 簡単, 分かりやすい, デバッグしやすい デメリット: プロセスの起動ってコストがかかるんや……

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

    ってのがあってだな PSGI もうperl がHTTP をしゃべっちゃう Perl ウェブ開発の中世~CGI と Plack の間~ - YAPC::Kansai 2017
  46. しかしプロセスの使われ方は しかしプロセスの使われ方は 変わっていない 変わっていない

  47. 同期的Prefork モデル 同期的Prefork モデル Pre(= 事前に)fork あらかじめリクエストを処理するプログラムのプロセスを複数立 てておき、リクエストが来たら暇しているプロセスに配る 1 プロセスだと1

    度に一つのリクエストしかさばけないが、 Prefork モデルはfork したプロセス分さばける
  48. Prefork モデルは他のLL でも使 Prefork モデルは他のLL でも使 われている われている Perl/PSGI -

    starman, starlet Ruby/Rack - unicorn Python/WSGI - gunicorn
  49. 同期的Prefork モデルが使われ 同期的Prefork モデルが使われ る理由 る理由 -> 簡単 -> 簡単

  50. マルチスレッドではないのでデータ競合で変数壊れるとか無い そもそも並行プログラミングを考えなくて良い、コードも上から 下に読んでいけば良い LB とインスタンスの関係と同じなので同じ考え方が適用できる 共有リソースがないのでサーバを横に簡単に並べられる => スケールアウト

  51. しかし同期的Prefork モデルに しかし同期的Prefork モデルに も限界が も限界が もし、一つのHTTP リクエストが数十秒、数百秒かかっていたら? Prefork モデルはfork

    したプロセス分さばける
  52. Prefork したプロセス以上のリクエストがHTTP リクエストの寿命の 間に殺到したときに破滅する

  53. 刹那的な場合はプロセス数以上のリクエストが来てもなんとかさばける

  54. ここに永続的な場合にプロセス数以上のリクエストが来るとレスポンスが詰まる

  55. 破滅です 破滅です 同時接続数が少ないアプリケーションでは破滅しないこともあります

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

    通信は刹那的な A. 普通のHTTP 通信は刹那的な プロトコルだから プロトコルだから
  57. 今までのHTTP では1 つのリク 今までのHTTP では1 つのリク エストが1 つのプロセスを専 エストが1 つのプロセスを専

    有する時間が短い。だからこ 有する時間が短い。だからこ れでやってこれた れでやってこれた
  58. 刹那的接続から永続的接続 刹那的接続から永続的接続 へ…… へ…… ロングポーリング, SSE, WebSocket... これらはすべて1 回のHTTP リクエストの寿命が長い

    従来のアーキテクチャではプロセスが枯渇する
  59. C10K の話をしています C10K の話をしています C10K = Connection 10,000 Problem サーバリソースはあるのにアーキテクチャの問題でリクエストを

    さばくことが出来ない問題 本来はHTTP Keep-Alive の文脈からも論ぜられますが、Keep-Alive はnginx とかが維持してくれるし、刹那的HTTP に 変換可能なのでアプリケーションの変更はいらない
  60. C10K の解法 並行プログラミングの導入 並行プログラミングの導入 イベント駆動(Node.js, AnyEvent, etc...) 1 プロセスで複数の接続を扱うために、処理を細切れにして動 かす

    コールバック地獄になる...
  61. ネイティブスレッド(Java 族, C 族, etc...) データ競合... マルチスレッドプログラミングはバグを知るのが 難しい... fork よりはマシだけれどネイティブスレッドも重いからスレッ

    ドプールが必要なのよねえ 今のPerl5 では使えない( そもそもithread は……) ithread はメモリ共有モデルではない
  62. 軽量スレッド(Erlang/OTP, Elixer, Go, etc...) もっとも今まで通りに書けるが1 プロセスで複数の処理が並行 で走るのは変わらないので、バグ潰しが難しい

  63. リアルタイム通信をやりたい リアルタイム通信をやりたい だけでアプリケーション全体 だけでアプリケーション全体 の言語やプログラミングパラ の言語やプログラミングパラ ダイムを変更するのは理にか ダイムを変更するのは理にか なっているか なっているか

  64. わからん! わからん! 場合による!! 場合による!! 自分で考えてくれ!!! 自分で考えてくれ!!!

  65. イチから作るか、動いている物に導入するか 今の言語で出来るかどうか 移行先の言語やパラダイムに精通している人がいるかどうか カネと時間があるかどうか 作ったもののパフォーマンスが出るか、検証できるかどうか

  66. 別件: アプリケーションと密結 別件: アプリケーションと密結 合の問題 合の問題 アプリ更新したいから再起動するぞ!!!! ↓ プロセスが殺されるから全部一旦切るで~~ ↓

    え?????? クライアントの再接続が一気に起こります
  67. 別の解法 => サーバプッシュだ 別の解法 => サーバプッシュだ け別にやる け別にやる Node.js でSocket.IO

    しゃべってRedis pub/sub でWebApp からつなげ る Go で似たようなことをgRPC/SSE でやる ActionCable Ruby on Rails でWebSocket をしゃべる機能 kuiperbelt <- 本日はコレの話 openfresh/plasma
  68. 第1 章まとめ 第1 章まとめ 同期的Prefork では大量の寿命の長い接続をさばくのは困難であ る 現実的にやるには並行プログラミングを導入するか、サーバプッ シュだけ別のサーバでやる手法が考えられる

  69. 第2 章 第2 章 kuiperbelt とは kuiperbelt とは

  70. kuiperbelt とは kuiperbelt とは WebSocket とHTTP/1.1 の相互変換プロキシサーバ by 独立したWeb サーバデーモンとして動作する

    Go で書かれているが使うのにGo の知識は要らない mackee/kuiperbelt README.ja.md
  71. 機能 機能 WebSocket の接続を維持する 及び 現実的にやるための仕組みの 提供 接続認証の方法 スケールアウト戦略

  72. 思想 思想 WebSocket をしゃべるサーバを直接インターネットに晒さない。 ロードバランサーに入れる 他の事例では直接インターネットにさらしているケースが多い 大量接続に耐えるWebSocket アプリケーションサーバ構築のコ ツ -

    pixiv inside
  73. 何故ロードバランサーに入れ 何故ロードバランサーに入れ る? る? LB に入れることで管理コストを下げる WebApp とWebSocket を疎結合にする

  74. 思想 思想 独自のデータ構造を作らない。HTTP の仕組みをそのままできる だけ使う HTTP がしゃべれるアプリケーションであれば適用可能

  75. 接続時の通信の流れ 接続時の通信の流れ

  76. 試し方 試し方 から最新版のバイナリを落としてパスを通す Go のコマンドやデーモンはバイナリリリースがあれば自分でビ ルドせずバイナリリリースを使うのが好ましい のチュートリアルを読むか、 のPlack アプ リを読むと良い

    GitHub Releases README.ja.md example
  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!"}'],
  78. 認証用のアドレスを設定して起動 さっきのを で保存して起動する kuiperbelt 用config に立てたサーバのアドレスを設定ファイル ( ) に書く 書いた設定を使ってkuiperbelt

    を起動する $ plackup --port=5000 app.psgi callback: connect: http://localhost:5000/ $ ekbo -config=config.yml
  79. メッセージ送信時の通信の流れ

  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, );
  81. もともとの動機 もともとの動機 Perl5 でWebSocket とかSSE を喋りたかったが、 AnyEvent とかMojo 使えばPerl5 でWebSocket

    使えるやん? あ、そのせいでイベント駆動プログラミングをしないといけなく なった…… DB のトランザクションかけようとしたら、混ざるんじゃないの これ?
  82. そこで思いついた そこで思いついた WebSocket の接続を維持するだけのサーバを作ればよいのでは? それが普通のHTTP API の口を持っていて、アプリケーションサー バからAPI を叩いてWebSocket に送るメッセージを送信すればよ

    いのでは?
  83. 先人の知恵 APNS::Agent APNS::Agent iOS 端末にプッシュ通知( スリープしていても届くと音がなって振 動するやつ) をサーバから送るAPI が独自の生TCP かつ、非同期型

    Net::APNS::Extended などの同期型のモジュールもあるが、Prefork のWebApp から送ると当然詰まる
  84. 先人の知恵 APNS::Agent APNS::Agent AnyEvent::APNS と言うモジュールもあるが、イベント駆動にしな いと AnyEvent::APNS を単体デーモンに切り出したのがAPNS::Agent

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

    のサーバにつながっているソケッ トにメッセージと送り先トークンを書き込む 3. プッシュ通知が届く HTTP でiOS のpush 通知を送れる未来がやってきた(APNS::Agent の紹 介)
  86. 偉大なところ 偉大なところ やることに合わせたアーキテクチャを使い分けれるようにデーモ ン化して切り出した 一つのことをうまくやる 土管に徹する UNIX 哲学 HTTP API

    というどの言語でも持っているようなインターフェイス で触れる 現在では というデーモンに思想は引き継がれてAndroid にも通知が送れるようになっています Gunfish
  87. これだ!と思ってWebSocket これだ!と思ってWebSocket でも同じことをやろうとした でも同じことをやろうとした

  88. kuiperbelt という名前の由来 kuiperbelt という名前の由来 もともとはロングポールをやろうと思ってComet って言う別名もあ るので、じゃあ彗星の起源ということでエッジワース・カイパーベ ルトから名前を取った 同じような言葉でオールトの雲というのがありoort は短いから良いなあと思ったんですがこの名前のOSS

    は既に あった
  89. kuiperbelt はWebSocket の接続 kuiperbelt はWebSocket の接続 を維持するデーモンとしてど を維持するデーモンとしてど のような課題の解決を行って のような課題の解決を行って

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

    サーバは繋いで良いクライアントだったらUpgrade する 3. WebSocket 接続を開始する 4. 適宜送ったり、受け取ったりする 5. 切りたいときに切る
  91. 課題 課題 認証の方法 誰に送るかの指定方法 送る内容の形式 切断検知

  92. 認証の方法 認証の方法 kuiperbelt に認証機構を実装するのも考えたが…… ユーザDB に接続する? OAuth する? 土管に徹したいので認証機構は他所に投げたい 一発目は普通のHTTP

    リクエストだからこれをWebApp にプロキ シすればいいのでは?
  93. コールバックを投げる コールバックを投げる クライアントからのHTTP リクエストをそのままバックエンドの WebApp に飛ばす 200 だったらWebSocket 開始 認証はWebApp

    に委ねることでkuiperbelt の実装自体はコンパクト になり堅牢になる
  94. 宛先の指定方法 宛先の指定方法 接続ごとに識別子を指定して欲しい気持ち 接続の識別子は認証時のWebApp のレスポンスのヘッダに という名前で送ってもらってそれを使う メッセージを投げるときも というヘッダ に入れてもらう

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

    のBody の内容をそのままWebSocket に書き込んでいる を指定すればバイナリも突っ込める msgpack を使っているプロジェクトもあります
  96. Body はApp のもの Body はApp のもの つまり何をどう送るかはWebApp に委ねられている => 土管に徹す

    る思想から なので送信オプションとかはHTTP ヘッダを使うようにしている
  97. 土管に徹すると何が良いか 土管に徹すると何が良いか 言語非依存になる Perl 専用のデーモンだとユーザが広がりにくい。OSS プロダク トにおいてユーザ数というのは力になる 役割が明確になる アプリケーション非依存になる

  98. 切断検知 切断検知 WebSocket はTCP なのでお行儀よく切断すれば接続が切れたこと を検知することが出来る アプリケーションによっては切断を知ることは重要なのでクライ アントから接続が切れたら設定したコールバックURL を kuiperbelt

    が叩くように設定できる
  99. が、お行儀よく切断されないことはモバイルの世界だと大いに有り 得るので、すぐに切れたことを検知する事はできない

  100. kuiperbelt での検知 kuiperbelt での検知 メッセージを送ろうとして書き込めなかったら切断と認定 無通信時間が一定秒数以上続けば切断と認定 WebSocket のping フレームをクライアントが一定間隔で送って くることが前提の機能

  101. 無通信時間による切断検知は使用プロジェクトの要望で追加した機 能です

  102. 第2 章まとめ 第2 章まとめ WebSocket に必要な要素をステートレスHTTP に分解すると普通の WebApp でもWebSocket を扱えるようになる

    土管に徹することで様々なWebApp でも使えるようになる
  103. 第3 章 第3 章 本番投入への道 本番投入への道

  104. 作り始めたときは趣味だった 作り始めたときは趣味だった 社内でリアルタイムゲームのプロジェクトが立ち上がったときに 「ぼくがかんがえたさいきょうのWebSocket アーキテクチャ」を 披露し、既に実装はありますと言ったら採用された

  105. 趣味プロダクトはそのままで 趣味プロダクトはそのままで は本番投入はおそらく出来な は本番投入はおそらく出来な い…… い……

  106. そもそもどこのサービスにも投入されていない ユースケースが妄想かもしれない Proof of Concept のつもり こういうの考えてみたけれどどう?っていう実装

  107. プロジェクトへの導入をしながら機能を追加していった スケールアウト用の機能 ベンチマーク 監視API, ログ

  108. スケールアウト( 複数台) 戦略 スケールアウト( 複数台) 戦略 何故複数台構成に出来るようにするか? WebSocket 受ける君が1 台だと1

    台落ちるだけでサービス全体が障 害に陥る(HA 目的)
  109. 何故複数台構成に出来るようにするか? 1 台のスペックが上げられる上限は決まっている またAWS だとスペックは倍倍に上がっていくのでちょうどいい スペックに設定できないというのもあります

  110. 先行事例 先行事例 PC ゲームなどのオンラインゲーム それぞれのインスタンスがPublic IP を持っていてクライアント に接続先IP を指定 同じ部屋の人は同じインスタンスに固めるなどが出来る

    インスタンス落ちたときのことをApp が考えないといけない
  111. 先行事例 先行事例 Node.js やActionCable など Redis pub/sub を用いる( メッセージキュー) WebSocket

    の接続を持つサーバが部屋ごとに作られたtopic を subscribe しておいて、WebApp 側が部屋のtopic に対してメッセ ージをpublish する Redis さえスケールさせれば、そうRedis さえ落ちなければ……
  112. やりたくないこと やりたくないこと WebApp からつなぐ先を指定させたくない 外から繋げられるPublic IP がそれぞれのインスタンスに必要 負荷の偏りが生じる

  113. やりたくないこと やりたくないこと 特定のミドルウェアに依存したくない Redis pub/sub を使うとスケール限界がRedis の性能になる インフラ構成でRedis が必須になる

  114. 必要なのは どの接続がどのインスタンス どの接続がどのインスタンス にあるかをクライアント にあるかをクライアント (WebApp) が知っておくこと (WebApp) が知っておくこと

  115. KVS のシャーディング方法を KVS のシャーディング方法を 参考にする 参考にする libketama とかtwemproxy とか クライアントがキーから値が入っているホストを特定して取って

    くる KVS 自体は分散されていることを別に知らなくても良い
  116. WebSocket の場合 WebSocket の場合 どこにつなぐべきかを接続時に指定すれば同じようなことは出来 るが…… やりたくないリストに入っている 前段にLB を立てる場合はどこに繋ぐかはランダムなので接続識 別子からどこに入っているかを導き出すことは出来ない

    スティッキーセッションを使えばランダムじゃなくすることは出来ますがどこに繋ぐべきかを指定するパタ ーンと同じになる
  117. 接続コールバックにアドレス 接続コールバックにアドレス を埋め込む を埋め込む callback のリクエストにkuiperbelt は以下のヘッダを付 ける X-Kuiperbelt-Endpoint: 192.168.100.1:9180

  118. 接続コールバックにアドレス 接続コールバックにアドレス を埋め込む を埋め込む これをWebApp 側は接続識別子と一緒に入れておいてどこにつな がっているかをKVS とかに保存しておく

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

    と接続識別子の変 A. ユーザID と接続識別子の変 換にKVS とか使うでしょ 換にKVS とか使うでしょ
  120. Q. 識別子= ユーザID とかにす Q. 識別子= ユーザID とかにす ればKVS 必要じゃなくない?

    ればKVS 必要じゃなくない? A. 1 人のユーザが複数のWS 接 A. 1 人のユーザが複数のWS 接 続持つケースがあって 続持つケースがあって
  121. これでスケールの上限は これでスケールの上限は WebApp 側の識別子解決の方 WebApp 側の識別子解決の方 法に依存する 法に依存する

  122. もしくはネットワークトポロ もしくはネットワークトポロ ジがフルメッシュになってい ジがフルメッシュになってい るのでそこも性能限界になり るのでそこも性能限界になり うる うる

  123. メッセージ送信時の通信の流れ

  124. kuiperbelt インスタンスが持っているSend API への接続数は WebApp 側インスタンスの数に依存する 超大規模環境でWebApp が多いと階層構造にするみたいなのが 必要そう? Perl

    はメモリを食わないので横の台数が少なくて済むので便利
  125. どこがボトルネックになりう どこがボトルネックになりう るかをちゃんと知るのが大切 るかをちゃんと知るのが大切

  126. ベンチマーク ベンチマーク 理論上OK でも実際どうなのかというのは実際に動かさないとわ からない.... 実際のアプリケーションで想定される負荷をかけてみる ミドルウェアのベンチをとっとけば他のアプリでも採用がすん なりいきやすい

  127. ベンチの条件 ベンチの条件 クライアントは指定した数だけWebSocket のコネクションを貼る 降ってきたメッセージはログに吐く WebApp 側は接続中の接続の識別子を取ってきてメッセージを送 るここは実アプリの挙動を真似すると良い n 人に同じメッセージを送るパターン

    メッセージサイズetc...
  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
  129. わかること わかること メモリ使用量はクライアント数に依存する CPU は秒間メッセージ数に依存する ここからチューニングポイントが分かる

  130. さらにクライアント数を伸ばしていくと... clients event CPU Mem(RSS) msgs/sec 20000 3333 47% 1400MB

    8666 40000 6666 130% 1500MB(4a383f2) 17880
  131. 40000 clients のところでメモリ使用量が減っている?けれど メモリが結構辛くなったのでチューニングを行いました

  132. メッセージを書き込むときに確保するバッファサイズを最適化し た use io.CopyBuffer by @fujiwara io.Copy makes 32KB buffer

    automatically, but it is too large for kuiperbelt use case.
  133. これらベンチマークはfujiwara さんにやっていただきました

  134. 監視系の機能も@fujiwara さんに入れてもらったものが多い

  135. 監視系のAPI 監視系のAPI 時間を返すだけのAPI ロードバランサーからのヘルスチェックに必要 kuiperbelt 固有の監視項目を返す 生きてるコネクション数/ エラー数 はこれを使っている 運用と監視は切っても切れない関係にあることがわかりますね

    mackerel-plugin-kuiperbelt
  136. ログはガンガン出す ログはガンガン出す ただしパフォーマンスにも影響するのでログレベルで調整できるよ うにする

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

    kuiperbelt はDEBUG にガンガンログを出している => WebSocket のデバッグがめんどくさいので開発中に躓いて出したログはだ いたい残している その代わりに運用中にはDEBUG やINFO のログは切っている Log::Minimal
  138. ログうるさすぎ問題 ログうるさすぎ問題 アクセスログをINFO で出していたけれどログ流量が半端なくて 他のログが埋まって機能しなくなった…… そもそも前段にnginx やALB がいるときはそっちでログが出せる のでは? 導入プロジェクトの人の要望から

    オプ ションを追加した
  139. デバッグ時には必要だけれど運用時には必要ないログみたいなのが ある

  140. そしてプロダクションへ そしてプロダクションへ カヤックでゲームをリリースするときは事前に専用のシナリオで 負荷試験を行っています kuiperbelt 導入プロジェクトはkuiperbelt 含めた負荷試験も行っ ている それもクリアして、本番で実際に動いています

  141. これで これで Production Ready Production Ready と言える と言える

  142. 第3 章まとめ 第3 章まとめ 本番運用できるミドルウェアにするためには、いくつかの機能追 加やベンチマークなど作り始めとは違う視点が必要 ミドルウェアは信頼されてなんぼなので、これらは必要な労力だ と思う

  143. まとめ まとめ ミドルウェア作りは産みの苦しみもあるけれど、概念作れるし小 説の世界観設計に似ている 俺がミドルウェアを作るからその上で面白いゲームを作ってくれ と日々思っています 一回概念を提唱するとそれについてくる人がいて助かる Thanks for @fujiwara,

    @shogo82148, and Users!
  144. 提供 提供 面白法人カヤックでは攻めのゲームづくりを一緒にする仲間を募集 しています!!! Perl とかGo とかUnity/C# とか。今やってなくても応募はタダ!!

  145. その他やったこと その他やったこと 本当にWebSocket をスマホが喋れるか検証 レースコンディション解消 Docker 対応 ALB が半死したときにhttp2 になっててkuiperbelt

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

    込みたい が、kuiperbelt を複数台構成にしたときは個別のコンテナにつな がるホストとポートをkuiperbelt 自身が知る必要がある
  147. 自分につながるホスト名の取 自分につながるホスト名の取 得 得 オプションにホスト名orIP アドレスを書くか、無ければ を使う コンテナ内ではコンテナ起動時のネットワーク設定によっては で正しいものが取れない

  148. 自分につながるホスト名の取 自分につながるホスト名の取 得 得 コンテナ起動時にconfig を書き換えてX-Kuiperbelt-Host を正しいも のにしている 以上はECS の場合です。k8s

    とかの場合はまだ試行錯誤中です。誰 か助けてくれ
  149. その他課題 その他課題 kuiperbelt 単体でスケールアウトさせる需要の声 ドキュメント 日本語ドキュメントはちゃんと書いてあるのだが ロゴがほしい 社外のユーザ 現状社内しかユーザはいない

  150. これからの展望 これからの展望 ekbo-router(cluster mode) 上り対応( 需要があれば) WebSocket 以外の対応 WebRTC が個人的に有望そう

    WebSocket over HTTP/2
  151. Net::Kuiperbelt, Alien::Kuiperbelt HTTP 喋れば良いとは言えライブラリがあったほうがみんな使 いそう Plack::Middleware とかで実現しても良いですが できるのか?

  152. 未実装の 未来の話: ekbo-router 未来の話: ekbo-router 専用デーモンをWebApp 側に立ててプロキシする 識別子の発行はekbo-router が行う

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

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