この記事はSupership株式会社 Advent Calendar 2016の3日目の記事になります。
弊社サービス、Sunychatのサーバーサイドを担当している @astapi です。
Sunnychatではアプリへの更新通知にwebsocketを使用しています。
この投稿では、下記2つについて書こうと思います。
- なぜ、websocketを使用しているのか
- どう実装しているのか
なぜ、websocketを使用しているのか
前提として以下のような背景がありました。
- ポーリングで5秒ごとに更新を検知するリクエストをアプリから行っていた。
- ポーリングを脱却し、更新がある時だけ、サーバー側から通知を行う仕組みを作りたかった。
結論を一言でいうと、要件を満たしていて、一番実装がしやすかった
からです。
他の候補としては、HTTP/2なのか、MQTTなのかぐらいしか調べてはいなかったのですが、調べたときの感覚は以下です。
- HTTP/2は調べてる段階では、サーバー側のライブラリが対応されていなかった。
- ブラウザでのケースはあるが、アプリでのケースがわからなかった。
- MQTTのウリとしては、QoSだと思っていますが、サービス要件としてそこまでの到達保証が必要なかった。
- 多少、失敗したとしても他の手段で更新可能
- websocketに比べて、実装イメージがサーバー、アプリともに薄かった。
websocketに関しては、使える状況ではない。という意見も見たりしますが、
それは、特定状況を含めた話しであり、且つ対象がWebブラウザなので、
アプリを対象にした場合の選択肢としては、アリだと思っています。
websocketがいいと思う理由が以下です。
- サーバーサイドの実装が楽。
- これについては後述します。
- アプリ側のhttpクライアントも対応している。
- アプリでの利用ならversion差異によって使えないという事を気になくていい。
- 通信内容はjson(にしている)なので普段使用しているAPIと変わらない感覚。
サンプルだったり、やってみた系の情報はよく見られますが、
実際にproductionで利用しているというケースは、どの選択肢でもあまり聞かないので、実際に実装してみてうまく行くかどうかは、わかんないな。という感じでした。
今考えてみると、アプリ側もクライアントが対応してるしwebsocketいけるよね。って感じで大した検証もせずにサーバーサイドを実装してたので、うまくいってよかったです。
どう実装しているか
言語: Elixir
フレームワーク: Phoenix
の構成で実装しています。
こちらのスライドでも紹介していますが、
Phoenixには、ChannelというPub/Subの仕組みがあり、その内部はwebsocketが使用されています。
Channelを使用して、通知したいユーザーに対して、websocketを通じてpushを行っています。
実装する内容としては、簡単で、
PhoenixではRailsのコントローラーの実装をするように、websocketを扱うことができます。
フレームワーク内部ではwebsocketの通信(json)をデシリアライズし、Elixirで扱うMapに変換してくれます。
その通信内容を元に、コントローラーにディスパッチしてくれて、レスポンスとしてjsonを返せばいいです。
ChannelのバックエンドのPub/Subには、Redisを使用しています。
PhoenixのデフォルトはPG2というErlangのライブラリが利用されていますが、
こちらを利用する場合、ErlangVMのNodeでクラスタを構成しないといけません。
そこの構築の仕組みを作るより、Redisサーバーを挟んだほうが楽だった。
且つ、性能も悪くないためRedisを選択しました。
Phoenixは、単体のサーバーで200万コネクションを実現しています。
ソケットの管理は内部で使用しているCowboyというErlangのサーバーです。
性能的にも、求めているものを充分に満たしています。
なぜ、Elixirなのか
- すでにサービス内の別の部分でElixirを使っていた。
- そのため、言語としての導入障壁は低かった。
- Elixirの軽量プロセスを用いたアクターモデルが、サービスに合っていた。
- Goは知らなかった。
- Phoenixでwebsocketを扱うのがすごく簡単だった。
- Erlangの大規模サービスの実績は多くある。
- 並列、並行処理が強い言語が来ると思っている。
最後に
Sunnychatでのwebsocketの利用事例を紹介しました。
今回のように技術選択を行うシーンで何を考えて、何故選択したか。を残しておくことは、次の選択で結構約に立つかも。って思いました。
振り返ってみると、「なぜ」が弱いと感じました。
色んな選択肢をしっかりと調べるのは結構大変で、割りと決め打ちでwebsocketを重点的に調べていたので、ひいき的な選択だったかもしれません。
結果的にはうまくいったのでよかったです。