nanisore oishisou

Webエンジニアあるまさんのゆるふわ奮闘記。

リアルタイムな通知を実装したいから色々調べてみた

こんばんわー。
ベル子だよー。眠くて瞼がくっついちゃいそうだよー。
頑張って調べすぎたよー。いつものことだけどねー。
何かを調べだすと時間を忘れてしまう病だと思うんだ、私。
とにかく今は、鬼かわいいパンケーキが食べたいってことしか考えられない。

では、まずリアルタイムな通知って何かというと

ベル子とイケメンが[ベル子SNS]というSNSを使っています。
ベル子がコメントをうpすると、イケメンのほうのブラウザ画面に
「セクシー美女からのコメントが1件あります」のような通知が
ページリロードなしで表示される。

というもの。
要するにTwitterのあれ。

まず、リアルタイムな通知を可能にする方法には、以下のものがある。

・Polling

古典的な方法で、一定時間ごとにサーバに更新を問い合わせる手法。
ページ全体を取得すると高負荷となるため、Ajax通信でリクエストし変更部分を更新するのが最近では主流。
リアルタイム性に欠ける。
サーバー負荷が高い。
無駄なリクエストが発生する。
といった問題点がある。

・Long polling(Comet)

Pollingではリアルタイム性に欠けるため、考えだされた手法。
クライアントから送信されたリクエストをサーバがいったん保留し、
サーバ側でイベントが発生した際に任意のタイミングでレスポンスを返す手法。
リアルタイム性は高い。
無駄な問い合わせは発生しないが、長時間HTTPコネクションを占有してしまう。
一度クライアントにPushしたら、またクライアントからのリクエストを待たないといけない。
タイミングによってはタイムラグが発生する。

・Server-Sent Events(Streaming)

Long pollingの発展版で、HTML5で標準化されている手法。
サーバーはイベントが発生したら、クライアントに断片データを返す。
レスポンスの一部を送信することによりクライアントとの通信が切れず
Long pollingと違い再度リクエストする必要がない。
新規格なのでブラウザがSSEに対応している必要があるが、IEは全滅。
Edgeも検討中でまだ対応はしていない。
polyfillで対応可能。

・Websocket

HTTPを拡張したリアルタイム通信を前提として双方向にデータが送受信できる通信プロトコルのこと。
小さなデータサイズで情報を送受信できるためHTTPに比べるとかなり通信コストが低い。
TCPコネクションを張りっぱなしにするため、ノンブロッキングI/Oサーバ(Node.js)が必要。
HTML5APIとして語られることが多く、現在はW3Cで勧告候補(Candidate Recommendation)となっているが、
最新の仕様はWHATWGのLiving Standard(後述)に記載されている。
IE10以降対応。

リアルタイムと聞いて、まず最初に思いつくのはWebsocket。

イケてる人たちが「これからはWebsocketでリアルタイムだぜ」と
2011年に私が某◯SNのHTML5特集をコーディングした時に言っていたのを、
うっすら覚えていた。
あれからすでに5年も経ってるから、もうスタンダードになってるよね?と思ったし
Laracastsでもテイラーが「最近、Websocketでつなげること多くなってきたよねー?」と言っていたので、当然がっつりスタンダードなんだろうねと思ったけど、いろいろ調べた結果、少し微妙な点もありそう。

そしてWebsocketがいいのではないかと思ったのには、もっと現実的な理由があって
Laravel5.1以降には、Websocketを使ってリアルタイム通信をする機構が
最初から備わっているのだ!よ!
デフォルトではPusherという外部サービスが、ブロードキャストドライバーに設定されてるんだけど
RedisとSocket.ioを使って自前で実装する方法も選択できる。さすがLaravel。
だから、Laravelに限定して言うと「実装が簡単」そう。
私にもできそう。一人でもできそう。かわいい女の子だってできちゃいそう。

ドキュメント
https://laravel.com/docs/master/events#broadcasting-events

イベントブロードキャストとは、サーバー側で発火したイベントをクライアント側に配信する機構で、
そのドライバーとしてPusher、Redis、Log(開発用)の3種から選択できる。
お好みのもの選べるのって嬉しいよねー。
女子はブッフェ好きだしねー。

Pusherを使えば、リアルタイム通信の部分は外部サービスにお任せできるので
サーバー負荷などの心配もしなくていいんだけど、もちろんお値段がかかります。
「実装も楽々で、サーバの負荷も気にしなくていいなんて、すごいわ〜。
でも、それってお高いんでしょう?」
って思うよね。

以下が価格表です。
会社で見たときは画面が小さくてフリープランが見えてなかったんだけど、
家で確認したら一番下にフリープランが書いてあった。
あと、コネクションという単位がよく分からず、これが1回の接続と仮定すると
「すごい高いな.....」と思ったんだけど、ポップアップの注意書きに『同時接続ユーザー数のこと』と書いてあった。だよねーだよねーじゃなきゃべらぼうに高いよねー。
そうだとするとログインユーザーが同時に1万人以下であれば
月々5万円以下で運用できそう。割と妥当なお値段でした。
「やだやだーお安いーー」とまでは言えないけど。

https://pusher.com/pricing

フリープランスタートアッププロビジネスプレミアムエンタープライズ
価格 無料 49$(約5,000円) 99$(約1万円) 299$(約3万円) 499$(約5万円) 要問い合わせ
同時接続ユーザー数 最大100 最大500 最大2,000 最大5,000 最大10,000 10,000以上
送受信メッセージ数 20万/日 100万/日 400万/日 1,000万/日 2,000万/日 2,000万以上/日

RedisとSocket.ioを使う方法は、タダでできるしいいかなーと思ったのですが、
サーバーの負荷がどのくらいかかるのか、正直よく分からない。

Socket.ioはWebsocketで繋げられない場合はLong pollingで繋げるらしいので、
ブラウザーサポート的には心配なさそう。

Websocketのほうがいろいろと負荷的にメリットが多いと書いてある場合が多いけど
比較的に新しい技術のため、やや心配性のベル子さんは実地検査データが欲しくて更に調べてみました。
http://blog.flect.co.jp/labo/2014/04/websocket-8124.html

ロードバランシングに工夫が必要とか、サーバを冗長化とか、SSL接続じゃないとダメとか
ファイアーウォールに注意とかいろいろ書いてあって、
インフラ強くないと怖そう。。。だ。。な。。
ただ、この人たちは絶対にゲームとか作ってる人たちだから、そこまで通知って通信量多くないよね?って気もする。
https://gist.github.com/nulltask/89e6f36e194c951697a0
http://uorat.hatenablog.com/entry/2015/08/30/190613

あったぞ、テストしてる記事!
Cometのネットワークリソースの消費量はWebsocketの2~3倍。
Cometの場合はクライアント数によって遅延が増える。
このためかどうか分からないけど、結構Cometについてはあまりいいこと書いてある記事がなかった。
OSリソース面でもWebsocketは優位そう。
でもWebsocketと比べちゃえば、当然って気もする。
http://builder.japan.zdnet.com/sp_oracle/weblogic/35044482/2/

しかし、問題はRedis。
RedisとはNoSQLに分類されるインメモリKVS(キー・バリュー・ストア)で
「驚くほどレスポンスが速い」という噂。


インメモリとはデータをハードディスクなどには書き込まずメモリ上で管理するしくみのことで、RDB などに比べ非常に高速にデータを出し入れできるという特徴があります。

実は、2年くらい前にドットインストールをみながら試してみたことがあるんだけど、
当時は「使い方は分かったけど、何に使ったらいいものなのか分かりません(ドヤ顔)」というのが感想だった。

サーバのメモリを圧迫したりしないのかな?
たぶんするよね?
でも、Pub/Subのセッション共有だけでしか使わないようだし、そんなに気にしなくていいのかな?

あと、Node.jsが接続を無制限に受け付けると、その分リソースを食うのでは?
WebsocketはHTTPみたいに同時接続制限がないと書いてあった。

よく分からないけど、Redis使ってるから、Socket.io専用サーバを立てればいいのか?


WebSocketサーバーを冗長化した際に各々のサーバーでsubscribeしておき、連携したいデータをpublishすればサーバー間連系はこの中継サーバーに委ねられます。

なるほど、難しい用語がいっぱいだね!


じゃあ、次は代表的なサービスたちがどんな手法でリアルタイム通知をやってるか調べてみるぞ。

TwitterのStreaming APIはServer-Sent Eventsの規格ができる前からあるやつだから
独自のフォーマットらしいけど、一応種類的にはServer-Sent Eventsらしい。
http://qiita.com/mpyw/items/664390c945af6c035547

ここで、一応、標準規格のServer-Sent Eventsについて調べてみる。
ブラウザ実装状況は以下のとおり。

Server-Sent Eventsのブラウザ実装状況
http://caniuse.com/#search=Server-sent%20events

MS Edgeは実装検討中のようだけど、IEは全滅。
Can I UseだとだとW3CではなくWHATWGのLiving Standardにリンクが貼られてたけど
W3Cのドキュメントを検索したところ、現在は勧告(Recomendation)になってる。

WHATWGのLiving Standardというのは、最新の仕様を断続的にアップデートしているHTMLの標準仕様書で、
ブラウザの中の人は、だいたいこちらの仕様を参照しているらしい。
W3Cの仕様更新は遅く、Living Standardから切り出されているということが、その要因のよう。
http://www.publickey1.jp/blog/15/html5whatwgw3c_tpac_2015.html

話がそれたけど、気にしない。気にしない。

サーバサイドpush技術としてのWebsocketとServer-sent eventsの特徴比較
http://qiita.com/suin/items/e33af700ceb678d40a67

だけど、そもそも論でごめん、Laravelでどうやって実装すればいいのかよく分からない。

実装例があったわよー、奥さん。
http://zrashwani.com/server-sent-events-example-laravel/#.VwqYwxOLRE4

次はfaceboook。
めっちゃ明らかなLong polling!

Long Pollingは、普通のPollingより負荷が高いことが、さっき読んだ記事で分かったけど、
これももしかしたら、一種のServer-sent events(独自)なのかも?
Server-sent eventsはLong Pollingの発展版と書いてあったし。

じゃあ最後にyammer。
これもLong pollingっぽい。

pollingのajax自体は3秒ごとに投げて、サーバー側で最大60秒保留しておいて返す。
みたいなことなのかもしれない。

なるほど、ゲームなどのリアルタイム性を要求される場合はwebsocketマストだけど、
通知くらいならLong polling(独自Server-sent Events)でいいのかもね。
あとはサーバにどれだけ負荷がかかってくるかってことか。。。
参考にしてる人たちはサーバ富豪そうだしな。。。

結論:
いやーいっぱい調べたけど、とりあえずAjaxで更新をチェックさせておけば、あとからLong pollingにはできそうだから、とりあえずそうするよ。
いろいろやって、あとでサーバのほうの設定でてこずっちゃうと困るし。


どうでもいいけど、テイラースウィフトのスタイルとユーモアセンスが羨ましすぎる。
http://www.gizmodo.jp/2016/04/taylor_swift.html

お疲れ様でした!!