WebSocketはいけてる!(^^v
というのが、初めて実際にWebSocketアプリを実装してみての感想です。
前回の予告通り今回はHerokuでWebSocketの話です。
ちなみにアプリ自体は前回のモノと同じです。
http://flect-papertrail.herokuapp.com/
OAuthでHerokuにログインして「heroku logs -t」相当のAPIでログを取得しながら、log-runtime-metricsの数字を拾ってリアルタイムにグラフ表示します。
拾う数字は<KEY>=<数字>という形式であればなんでも良いのでPostgreSQLの出力情報や自アプリで出したログもグラフ化することが可能です。
WebSocketのサンプル題材としてはそこそこ面白いと思うんですが。。。
残念なことにHerokuのログは直近1500行しか取れないので、それなりにアクセスのあるアプリだとグラフにプロットすべきポイントが数個しか取れないという落ちがあります。(^^;
お暇な人はしばらく放置してグラフを眺めてみてください。別に見なくてもここから先の本題を読むのにまったく支障はありませんが。(^^;
★ WebSocketアプリケーションの作成
といっても、ここでは作り方の解説はしません。。。(^^;;;
普通にNode.jsとかPlayとかでWebSocketアプリケーションを作成すれば、それがそのままHeroku上でも動きます。
唯一Herokuアプリ特有の処理が必要なのは、HerokuのWebSocketには30秒でのタイムアウトがあるので、それを回避するために定期的に通信を行わなければならないという点です。
これを実現する一番簡単な方法はクライアント側JavaScriptのsetIntervalで定期的にダミーリクエストを送信することだと思います。
Play2(Scala)でのWebSocketアプリケーションの作り方は気が向いたら、また別途まとめます。(^^;;;
★HerokuのWebSocket
現在HerokuのWebSocket機能はβ版であり、使用するためにはアプリ毎にコマンド
heroku labs:enable websockets -a myapp
を実行する必要があります。
このコマンドを実行することでWebDynoのフロントにあるLoadBalancerがWebSocket対応版に切り替わります。
Dynoの機能として何かが追加されるわけではなく、入口の方が変更されるわけです。
このアーキテクチャのため、コマンドでWebSocketを有効にした後に実際にWebSocketが使用できるようになるまでに若干の時間(DNSの切り替え)を要することがあります。(とドキュメントに書いてあります。)
またAddonとしてSSL Endpointを使用している場合はWebSocketを有効にできません。これはSSL Endpointはほとんどの場合独自ドメインと併用されており、ユーザーの管理するDNSでHerokuのSSL Endpointを指すように設定されているので、そのエンドポイントを勝手に切り替えるわけにはいかないためと思われます。
この場合、一度SSLEndpointを解除して、WebSocketを有効にしてから再度SSL Endpointを追加します。もちろんDNSの再設定も必要なはずです。(試してないですけど多分。。。)
★マルチDynoとWebSocket
WebDynoを複数起動した状態でWebSocket接続を確立するとそこでの送受信は常に同じDynoに対して行われます。
逆に言うと他のDynoからはその接続は見えません。
なのでWebSocketのサンプルとしてよくあるチャットルームみたいなものを作ろうとした場合、各ユーザーが同じDynoに接続されるとは限らないので困ってしまいます。。。(--
もっとも、これはHerokuに限った話ではなくスケールアウトするWebSocketアプリには常につきまとう課題です。ざっと検索したところ、この場合Redisのpub/sub機能を使用するのがセオリーのようです。
★Dyno再起動とWebSocket
普通に接続が切断されるだけでしょうね。。。
少なくともコマンドで「heroku restart」を叩いたところでは問答無用で切断されました。
これはよくよく考えると結構悩ましい問題です。Dynoにはデイリー再起動があるので、だいたい1日に1回再起動があるわけですが、このタイミングに存在した接続を救済する方法は多分ありません。
対処方法としてはクライアント側でサーバからの切断を検知したら再接続するとか?
けっこうめんどくさそう。。。(--
だけど、この辺どうなんでしょうね?これもHerokuに限らず他の環境でもWebSocketサーバからでも不当に切断されることはありそうな気がするので、まっとうなWebSocketアプリケーションはサーバーサイドからの切断に備えて必ずリトライを実装しておくべきなのかもしれません。
経験者のお話を伺いたいですな。
★モバイルとWebSocket
つねづねかねがね思ってたんですが、劣悪な通信環境や端末がスリープした場合にはWebSocket接続はどうなるの?という疑問があったので何パターンか試してみました。
テストに使用した端末はELUGA P-02E(Docomo, Android 4.1.2, 3G/LTE)とiPad2(iOS7, WiFi)です。
Androidは標準ブラウザがWebSocketに対応していないのでChromeを使用しています。(Can I UseによるとAndroid標準ブラウザも4.4からはWebSocketに対応しているらしいです)
- Android スリープ
スリープしても通信は継続されます。
数分放置しても復帰時に接続は継続されておりおそらくメッセージの欠落もありません。(てことは電池の消費も早くなる気も。。。)
ただ、何回かテストした中で一度だけ接続切れました。
- Android 劣悪通信環境
ほとんどネット接続ができなくなる通信砂漠を経過しても通信は継続されます。
おそらくメッセージの欠落もありません。
どこかでバッファリングされているってことですかね。
どうでもいいですがここで言う通信砂漠は東急日吉駅近辺のことです。
- Android 再起動
切断され、再度ブラウザを開いた時にページ自体がリロードされます。
- iOS スリープ
切断されます。
- iOS WiFi電源断
切断されます。
- iOS 再起動
切断され、再度ブラウザを開いた時にページ自体がリロードされます。
□□□□
だいたいこんな感じです。もっと接続切れるかと思ってましたが、Androidは予想以上に頑張ってました。(^^;
網羅的にテストする気はまったく無いんですが、この結果だけからでもだいたいの傾向は見えてきます。
まぁ、切れる時には切れる(雑)ってことですね。(^^;
そしてそうであれば、やはりクライアント側からのリトライは必要な気がします。