読者です 読者をやめる 読者になる 読者になる

人権真骨頂

とくがたかいことでゆうめい

サイバーエージェントのインターンに参加してポーリング撲滅しようとしてきた

f:id:upamune:20170407135615j:plain

サイバーエージェントのインターンに行ってきた(3回目).

FRESH!(フレッシュ) - 生放送がログイン不要・高画質で見放題stormcat24 さんの元でインターンしてきた(2回目).

何をしたか

プロジェクトにある ポーリングを撲滅する というミッションがあり,撲滅するための基盤を作成した.プロジェクトには,状態を監視するためにいくつかポーリングしているところがあり,それを撲滅するための基盤.

作ったやつはこれ.

github.com

RedisをバックエンドにしたgRPC Stream or SSEでサブスクライブできるイベント配信基盤.

Plasmaとは

このミドルウェアには, Plasma というかっこいい名前がついている.

このミドルウェアの命名については結構悩んだ.

出た案はこんな感じ.

  • Raindrop → イベントが降ってくる感じ
  • Merlion → 口からドバーッとイベントが流れている感じ
  • Nagashi Somen → 水の流れ(ストリーム)に乗って素麺(イベント)が流れる感じ
  • Tokoroten→ イベントがグッと押し出される感じ

そんなときに, メンターから plasmaball という案が出た. 画像検索すると以下の画像がでてきて,

f:id:upamune:20170407141122j:plain

各クライアントとコネクションを張り続けてる感がすごくて,じゃあそれでという感じで決まった.

このイメージを元に,デザイナーさんがPlasmaのロゴを作ってくださった 🙏

f:id:upamune:20170407140201p:plain

良い.

Plasmaのアーキテクチャ

f:id:upamune:20170407142252p:plain

このアーキテクチャ図はメンターが作ってくれた 🙏

先にも書いたけど,PlasmaはRedisをバックエンドにしたイベント配信基盤でgRPC Stream, SSEを利用してイベントを配信する.

Goのクライアントはこちら.(RedisにPublishするだけ)

github.com

汎用的になるようにイベントのデータの型を json.RawMessage にしていたが,これでドハマりした.

なぜか,サービスにこのクライアントを入れてイベントをPublishするとなぜかData部分のJSONがbase64エンコードされている……. 結論から言うと,Go1.8未満のバージョンで json.RawMessagejson.Marshal するとbase64エンコードされてしまうのが原因.

以下はコードは同じでGoのバージョンだけ変えたやつ.

↓Go1.7.5

https://wandbox.org/permlink/hyeFPHPwgHSkh7Q0

↓Go1.8

https://wandbox.org/permlink/5Lmr35XIoCC0lm7g

Go1.7.5ではbase64エンコードされていることが分かる. 手元のマシンは1.8でサービスが動いているのは1.7だったのが悪かった. 1.8未満のバージョンでもポインタをMarshalするようにするとうまくいく.

どんな構成にするかは結構悩んだ.iOS, Android, Microservices間はgRPC Streamでいいかってすぐ決まったけど,問題はフロントエンドをどうするか.双方向通信はやる必要なくて,サーバー側からイベントを送りつけるだけで良いので,SSE(Sever Sent Event)を利用することにした.

gRPC StreamとSSEを利用することになったのは良いが,どうやってサービスを提供するか悩んでいたときにcmuxというライブラリを見つけた.

github.com

cmuxはプロトコル(HTTP1, HTTP2)やヘッダーを覗いて,どのサービスに流すかルーティングできるライブラリ.例えば,HTTP2でヘッダーに content-type:application/grpc とあったら, gRPCのサービスに流すみたいなことができる.

https://github.com/openfresh/plasma/blob/381100978b2d0e8add8cdf3f81a1b29f784a4a28/main.go#L178

これを利用して,gRPC StreamとSSEを同一ポートで提供するようにした.

大体の実装は最初の一週間ちょいで終わって,それからテスト書いたり,Makefile書いたり,docker-composeで利用できるようにしたりCircleCIで回すようにしたり環境を整備した.それから社内のGHEからGitHubに放流した.

GitHubに公開した後は,高速化したり,メトリクスを取れるようにしたり,ロガー追加したりした.

GitHubで公開されているので,インターン終わった後も貢献できてべんり. Issueとか上げてもらえれば ベストエフォートで対応したいw

Plasma のユースケース

ユースケースについては,メンターのブログに紹介されているので引用する.

SUBSCRIBE型のミドルウェアなので、多くのクライアントが特定のイベントを注視したいといったケースで力を発揮する。例えばFRESH!でいえば、番組のViewが放送前→放送開始になるといったケースでの利用を想定している(他にも色々ある)。

gRPCとServer-Sent Eventsでサーバプッシュできるplasmaを公開しました - tehepero note(・ω<)

こんな感じ.FRESH! でのポーリング撲滅プロジェクトに少しでも貢献できたなら嬉しい.

感想

実は冒頭でも書いたけど,サイバーエージェントでのインターンは3回目で去年のまったく同じ時期にサイバーエージェントの違うプロジェクトでインターンしてた.去年は配属されてサービスのアーキテクチャとか説明されたけど,ロードバランサーさえ分からず大変だったw 1年経って,またインターンしてみて,まぁ去年よりは成長したのかなと実感できて良かった. 自分はインターン中,これだけに集中させてもらえたけど, 普通は色んなチケットに手を付けながらやるもんだよなーと思い,他のサーバーサイドの方の凄さがわかった.

あとこれは,同じインターン先にプロジェクトにもう一回行ったからわかったことだけど,前のインターンのときに作ったマイクロサービスがちゃんと使われてることを確認できてよかったw

前のインターンの記事はコチラ.

blog.upamune.com

期間的に実際にポーリングが撲滅されていくところまでは見届けることはできなかったけど,後は頼みました….!!!!!!!!!