2014年4月22日 (火)

WebSocketアプリのモデルを考える

ども。  
Salesforce Hackチャレンジに参加していたQuizarは安定の選外でした。。。 
ま、Salesforce1まったく関係なかったから無理もない。(^^;  

さて、それはさておきQuizarの製作を通してWebSocketを使えば今まで世の中に存在しなかったようなアプリを作れる可能性があるということを実感したわけですが、現実にはほとんどWebSocketアプリは開発されていません。

こうした状況の最大の原因は世の中にWebSocketアプリ開発のためのモデルやフレームワークがほとんど存在しないためだと思っています。

現状では低レベルのAPIだけはあるものの、それしかないからその上のモノは全部自分で作らないといけないわけです。
Quizarの開発過程でやったことだけでも、メッセージのルーティング、リクエスト/レスポンスの対応付けなど通常のWeb開発ではやらないようなプログラミングをかなり行っています。

状況としてはhttp/1.0の頃にServlet APIだけで開発していた極初期のWebアプリ開発に近いかな?
あるいは、ひと固まりのメッセージがあるだけでそこに何の付帯情報もないということを考えると生ソケットで直接メッセージを読み書きしている状況に近いという気も。(^^;

いずれにせよこの状態から一本のアプリを作り上げるのは結構な労力なわけで、逆に言えば、になんらかのモデルとフレームワークがあればWebSocketアプリ開発のハードルは一気に下がる気がします。

いくつかのアプリ開発を通してなんとなくその形が見えてきはじめているので、今回はそれをざっくりまとめてみます。


★ モデル

多分WebSocketアプリのモデルはほとんどすべてルームモデルに収まります。

「ルームモデル」という言葉は小西の造語ですが、とりあえず以下のように定義します

  • アプリ空間に複数のルームがあり、
  • ルームには複数の人が入室でき、
  • ルーム内にいる人同士がリアルタイムにコラボレーションできるアプリケーション

シンプルな定義ですが、それだけにほとんどのWebSocketアプリはこのモデルにあてはまるはずです。

Quizarは典型的なルームモデルアプリケーションですが、ルームの中にいるのはクイズの出題者と回答者という役割の違う2種類の人達です。
このようにルームに入室した各人の役割は複数あり得るので

  • ルーム内にはロールの異なる複数の人が存在して良い


というのも定義に加えて良いかもしれません。


★ フレームワークに必要な機能

次にルームモデルアプリケーションを作成するために必要な機能を考えてみます。
サーバー側にルームを管理する機能が必要なのはもちろんですが、最低限メッセージのフォーマットを決めておかないとリクエストのハンドリングもままなりません。
また、クライアント側も必然的にSPA(Single Page Application)となるのでWebSocket以外にも多くの機能が欲しくなります。

ここではとりあえずメッセージ、サーバー、クライアントの3つにわけて必要と思われる機能を列挙してみます。(細かく説明し始めるとあまりにも長くなりそうなので。。。)

ちなみにこのリストもQuizarの開発経験からの逆引きです。

● メッセージ

  • JSON形式のエンベロープとする
  • リクエスト毎にIDを持つ
  • リクエストのコマンドが識別できる
  • 任意複数のパラメータが渡せる
  • レスポンスのエラーが識別できる
  • JSON、HTML、Textなど複数の形式のレスポンスを返すことができる


● サーバー

  • 複数のWebSocket接続を束ねるルームを持つ
  • ルームの永続化(RDB等へのルーム情報の保存)機能は範囲外
  • スケールアウトするための外部のPub/Sub機能を利用できる
  • ルーム内ユーザーへのメッセージブロードキャスト
  • ブロードキャストメッセージのフィルタリング
  • メッセージのルーティング
  • メッセージのロギング


外部のPub/Subは今のところRedis以外に知りませんが、差し替えられるように抽象化はした方が良さそうです。

● クライアント
クライアント側ではルームモデルに限らずSPA全般で必要となる機能も含みますが、それぞれの機能は独立して使えるモノとします。

  • WebSocketをAjaxライクに使用できる(リクエストに対応するレスポンスを取得できる)
  • メッセージのエラーハンドリング
  • リクエストにひもづかないメッセージのルーティング
  • サーバーサイドからの切断時の再接続
  • アイドル状態の識別とイベントハンドリング
  • プラグイン可能なデバッグログ
  • ポーリング
  • WebStorageでのテンプレート管理
  • PushState対応


サーバーとの通信はAjax併用でも良いんですが、多分全部WebSocketでやる方がシンプルだし速度面やセキュリティ面でも優位があります。(これはそのうち別に書きます。)

逆に面倒な部分としては切断時の対応とデバッグ(ブラウザのWebコンソールでメッセージを見ることができない)があるんですが、これらをうまくフレームワーク側で吸収できればメリットだけを享受することもできるはずです。

□□□□
とりあえず、こんなところでしょうか。
なんか忘れてる気もしますが、まぁそれはおいおい(^^;

実際にはQuizar開発当初からフレームワーク化を意識していたので、上に書いた内容は概ね実装イメージがあります。(もちろんブラッシュアップや再考が必要な部分も多々ありますが。。。)

サーバーサイドはWebSocketとRedisのPub/Subが使えれば何でも良いんですが、とりあえずPlayで。(他でも作るとしたら多分次はNode.js)

というわけで、6月末を目途にフレームワークとなんか適当なルームモデルアプリを作ります。(^^v



2014年4月16日 (水)

HerokuのWebSocketでC10Kに挑戦(後篇)

割り込みによって書けてなかったHeroku C10Kの続きです。


テスト実施から少し時間が空いてしまったため、改めてまとめようとすると疑問点や追試したいこともあるんですが、当面これ以上時間を割くことができそうにないのでざっくりとまとめてしまいます。

 

★前篇のおさらい

前篇の内容から今回ポイントとなる点をピックアップすると以下のようになります。

  • 1Dynoでさばける同時接続数は400前後。
  • それを越えるとH11の接続エラーが返ってくるようになる。
  • 雑なテストプログラムで25台のDynoに1万クライアント接続を試みたところ少なくとも5600位は繋がったっぽい。


「繋がったっぽい」というあいまいな表現になっているはテストプログラム側で何を持って成功とみなすかという点をはっきりと決めていなかったためです。
エラーが発生しなければ試行数をそのまま成績と判断できますが、途中でエラーが発生した場合そのテストがどの程度の成績を残したのかということを判断する基準がなかったんですね。

なので、それを踏まえてテストのレギュレーションは以下のように決めました。

  • ルームを100個用意する
  • 各部屋に3秒に1人ずつ、100人(100クライアント)入室する(最大時同時接続1万クライアント)
  • 各クライアントは5秒に1回チャットメッセージを投げる(合計100万メッセージ)
  • 各ルームに最初に入室したクライアントの受信したメッセージ数の合計をテストの得点とする(満点は100万点)


各部屋では100人が100回発言するのでエラーがなければ最初に入室した人は1万回メッセージを受信するはずです。
負荷があがってくると入室(WebSocket接続)時にエラーとなる可能性がありますが、負荷のかかっていない最初の入室者がエラーとなることはほとんどありません。

つまりエラーが全く起こらなかった場合の各クライアントの得点は10000点(100メッセージ × 100クライアント)で、クライアントの接続エラーが起きた場合は-100点(そのクライアントは一度もメッセージを発信しないから)、個別メッセージの送受信でエラーが発生した場合は-1点となります。
なんらかの理由でこのクライアントのWebSocket接続が途中で切断された場合は、以降のメッセージは受け取れないので、この場合は極端に低い得点となる可能性もありますがここまでの経験則から一度確立したWebSocket接続はかなり安定して動作することがわかっているので、その場合もそれをそのまま得点とすることにします。

ちなみにテストプログラムは前回使用したものから以下の改良を行いました。

  • WebSocketクライアントをjava_websocketからAsyncHttpClientに変更
  • 接続エラー時には最大10回のリトライを行うようにした


ノーマルSocket + Thread監視のjava_websocketはやはりメモリ消費量が多かったので1プロセス内でたくさんの接続を作れなかったのと、前回のテストからH11はリトライである程度回避できるのではないかと予想したからです。

テストは25Dyno(1X)と30Dyno(1X)の2回行いました。


★ 1回目 25 Dyno(1X)

得点: 795555

ぎりぎり8割いかなかった。。。(--
しかし、意外と良い数字という気もします。
傾向としては以下のような感じです。

  • 各クライアントの最低点は4152、最高点は9687.
  • 得点分布は5200点半以下(24クライアント)と8300点以上(76クライアント)にわかれている
  • 6000点台、7000点台を取ったクライアントは何故かいない
  • 満点(100000点)を取ったクライアントもいない
  • H11は少し発生しているがリトライで接続できているらしい
  • 多分接続自体は1万クライアント全部成功している
  • IOException多数


得点の低いクライアントは途中で接続が切断されています。サーバ側のログを見るとその頃に複数のDynoでIOExceptionが多数記録されていますが原因は不明です。
その後しばらくはエラーなくテストが進行していますがその理由も謎です。

また、10000点を取ったクライアントがいなかったことも特徴的です。はっきりとした数字は数えていませんが前回のテストでは10000点を取ったクライアントがかなりあったはずです。
これはクライアントをjava_websocketからAsyncHttpClientに変更した影響かと思っているんですが、過負荷な状況ではNonBlocking I/Oはメッセージの一部を取りこぼすことがあるんでしょうか???

どなたか知見のある人は教えてください。(^^;;;


★ 2回目 30 Dyno(1X)

考察もそこそこに2回目のテストへ。今回テストサーバも結構な数のDynoを使っていますが、負荷をかけるクライアントの方では2XDynoを100台使っていたり、Redisもいいお値段のものをテスト実行中のみ追加していたりする関係でテスト実行にかかる時間は1秒でも短い方が良いのです。(^^;

2回目は変更した条件はサーバ側のDyno数のみ30台。結果は。。。。


得点:999976

おー?!マジでか???
まさかのフォーナイン!ほとんどのメッセージが拾えています。

傾向としては以下のような感じ

  • IOException激減
  • 発生しているIOExceptionは「broken pipe」とか「connection reset by peer」とか
  • H11未発生


1Dynoあたりの平均接続数は400クライアント(10000 / 25)から333クライアント(10000 / 30)に下がってますが、それでこんなにも結果が違うんですね。

ちなみにRedisの負荷はサーバ数が増えれば大きくなるはずですが、こちらは何の問題もなくリクエストを捌いていたようです。

これ、タイトルにある「HerokuのWebSocketでC10Kに挑戦」はとりあえず達成と言っても良いんじゃないでしょうか?
まぁ金で解決しただけと言われればその通りなんですが、IOExceptionはアプリ側の問題なのでチューンすればもうちょっと少ないDyno数でC10Kを達成できそうな気はします。
あとWebSocketアプリ限定で使えるもっと効率的に負荷分散する方法も思いついているのでこれは多分そのうち書きます。


★ その他補足事項

どこに書こうか迷ったんですが、収まりの良いところがなかったのでここで。

前回、「1dynoあたりの接続数をもっと増やせないか?」という問い合わせをHerokuに対して行っているということをちらっと書きましたが、その回答が実は来ています。
回答を要約すると


とりあえず同時接続数増やしたよ。我々の目標とする数値には届いていないのでまだルーターの改修は続けるけど、前よりは格段に接続数増えたはず。試してみれ


て言う感じです。
で、上のテスト結果はおそらくその改修が効いた状態でのテストです。(どこかでもう全部のアプリで有効になっているという文章を読んだ気がするけど、探しても見当たらないので見間違いだったかも。ChangeLogにはあがってないので、ルーター改修はまだ作業中かもしれません。)

1Dynoで追試を行ったところH11に対してリトライをかければ800~1000位は繋がるようなので、たしかに以前よりはずっと同時接続性があがっているようなんですが、いまいち結果が安定していない気がするので、とりあえず結論は保留です。

2X Dynoや PX Dynoを使ったテストもそのうちするかもしれませんが、早くても来月以降になると思います。(何故なら2X Dyno100台を3時間あげるとそれだけで600Dyno Hourの消費になるから。。。(^^;;;)

2014年4月 9日 (水)

続OpenSSL祭り - 現在の状況は多分こんな感じ

昨日ブログを書きあげた後で素晴らしいまとめ記事を見つけました。

http://d.hatena.ne.jp/nekoruri/20140408/heartbleed

もうHeartbleedに関してはこれだけ読んでおけば良いんじゃないかと。(^^;

昨日書いた自分の理解はおおむね合ってたようですねぇ。。(^^v
ハッハッハッハッ。。。最悪です。

特に攻撃を受けた側が、攻撃されたかどうかを判断する術がないというあたりがいかんともしがたい。(--

技術情報に関しては上記でもう十分な気がしますが、イマイチ世間(非IT含む)に今回の件の深刻さが伝わってない気がするのでちょっとたとえ話をしてみたいと思います。
若干大袈裟な表現と感じるところがあるかもしれませんが、それはワザとです。
不安になったら自力で調べてみてください。


あなたは自分の大切なモノを保管するために部屋を借りました。その部屋には世界で一番売れている錠前が付いているので安心です。(^^v

ところが!

その錠前には必殺のピッキング技があって全ての錠前を鍵なしで開けることが可能だったのです。ナニ―(+o+)

あなたはあわてて錠前屋を呼んでピッキング対策をを行ったので、現在ではそのピッキング技は使えません。しかし、そのピッキング技が有効だった期間は約2年あります。。。
なにしろ世界中で使われている錠前だったのでもう世界中大パニックです。今のところ実際に被害があったという話は聞かれませんが、今まで安全と信じていたものが実はそうではなかったということが問題です。

あなたは改めて部屋を見渡してみます。どこにも侵入者があった形跡はありません。しかし部屋の中にはマスターキーもあるので、侵入者がそれを持ち出して合鍵を作った可能性は否定できません。

さて、あなたは錠前を交換するべきでしょうか。。。。




。。。というのが、現在の状況であるというのが僕の認識です。

その部屋が自分のものしか置いていない場所であれば、鍵を取り変えないという選択もあるかもしれません。実際のところ本当に侵入者があって鍵を持ち出した可能性というのは極めて低いだろうとも思います。

では、これが自分の部屋ではなく貸倉庫のような場所だったとしたらどうでしょうか?

あなたは貸倉庫を運営していてお客に保管スペースを提供しています。その部屋には世界で一番売れている錠前が付いているので安心です。(^^v

ところが!

(以下同文)

さて、あなたはこの倉庫の錠前を交換するべきでしょうか。。。。




いかがでしょう?普通の感覚であれば鍵が交換されない限りこの倉庫を使おうとは誰も思わないんじゃないでしょうか。。。僕の感覚では鍵を交換しないという選択肢はありません。

さらに付け加えるとすぐにピッキング対策を行ったあなたはまだ良心的な方で、実際にはピッキング対策を行わないままその錠前を使い続けている人が山ほどいるのです。。

。。。あー、書いててまた憂鬱になってきた。。。(--

実際のところ自分の使っている銀行やクレカのサイトでは現時点でこの問題に対して何のアナウンスも出ていないので、そろそろ「お前らのところは本当に大丈夫なのか?!」と問い合わせようかと思っているところです。(このブログのURLつけて)

ちなみにMoneyTreeという銀行口座のまとめサービスは現在サービスを停止しています。
お金を扱うサービスではこれ位の配慮は欲しいですよね。(他人事ではないのですが。。。)



もう本当に世の中のすべてのサイトでHeartbleed対応がどうなっているのかを明記してほしいですわ。。。(--


ところでCA(ここでいう錠前屋)は証明書(鍵)の無償交換に応じているところが多いようですが、あんたの所のリクエストフォームはHeartbleed対応されているの?っていうこともとても気になります。。。(--

(4/10追記)

若干文章修正しました。テクノロジー色の強いサービスを中心に徐々にパスワード変更を推奨というメールが届き始めています。(自分がほとんど使っていないので)ショップ系のサイトの対応がどうなっているかはわかりませんが、何のアナウンスも無いところには自分から問い合わせても良いでしょう。

2014年4月 8日 (火)

OpenSSL祭り(CVE-2014-0160)

ども。
本当はC10Kの後篇を書こうと思ってたんだけど世間はOpenSSL祭りなので、それに乗っかったエントリを書いてみる。

なお、これから書く内容はあくまでも小西の個人的な理解なので、正しいという保証はどこにもありません。

言っちゃなんですが、セキュリティとか暗号とかはどっちかと言うと苦手分野です。(^^;;;

しかし、書くことによって自分の理解が進んだり新たに気付くことがある。という経験則があるので、とりあえず書いてみることにします。

いつにもまして、ツッコミ歓迎


★概要

http://heartbleed.com/

にまとまってます。
ざっと僕の理解(くどいようですがあくまで個人的理解)をまとめると以下のような感じです。(上記サイトに書いてあることだけでなく、他のサイトで見た内容も含まれています。)

  • OpenSSLに深刻な脆弱性が見つかった
  • この問題はSSL/TLSプロトコルの問題ではなくあくまでOpenSSLの実装の問題
  • 過去2年間のOpenSSLリリースすべてにこの脆弱性が存在する(1.0.1以降)
  • この脆弱性を利用するとSSL通信を行っているホストのメモリを読むことができる(一度に読めるのは64Kまでだが、繰り返し何度も実行することができる)
  • OpenSSLを使っている著名なWebサーバとしてはApacheやnginxがある
  • 問題のあるバージョンのOpenSSLがバンドルされているOS多数


。。。ダメじゃん。。。(--

コンピュータ上のすべての処理は一度メモリを経由するわけで、それを考えるとSSL証明書とかクライアントから送信したユーザー名とパスワードとかクレジットカードの番号なんかまで全部どこかのタイミングでメモリに載るわけで、可能性としてはそれら全部を盗むことができるわけです。

。。。ホンマかいな。。。(--

 

★サーバがJavaなら大丈夫?

いいえ、そんなことはありません。
クライアントから直接Java製のサーバに繋いでいるのであれば大丈夫なはずですが、

  • フロントにApacheがいる
  • ELBをhttpモードで使っている
  • Herokuを使っている


全部アウトです。
Apacheは言わずもがな、Herokuのフロントエンドはnginxのはずですし、ELBも問題があることがアナウンスされています。

ロードバランサに脆弱性がある場合、攻撃者が読めるのはロードバランサのメモリまででバックエンドにあるアプリサーバのメモリは読めません。

しかし、HerokuもELBもSSLをロードバランサがほどいてバックエンドとの通信はhttpで行っていたりするので、そこでの通信内容はメモリに載ります。

逆に言うと

  • 一度も通信に載らないHerokuの環境変数などが漏洩することはない(言語は関係ない)
  • ELBをTCPモードで使用している場合は問題ない


と思ってるんですが、今のところ誰からも同意を得られていません。。。

。。。ホントにそうか???。。。

★ベンダの対応状況など

ネット上の情報によるとAmazon Linuxはパッチがでたようですね。

http://qiita.com/tachiba/items/83e5fd31d06e6577abb3

ELBはまだのようです。

Herokuの対応状況はstatus.heroku.comで確認できますが、SSLアドオンが影響受けるので対応終わったら証明書を再発行しろとか書いてあります。

https://status.heroku.com/incidents/606

。。。マジでか。。。(--

Heroku Postgresを再起動するというメールも来てるらしいですが、そういえばPostgreSQLとの通信もSSLでした。。。(--

 AWSとかSalesforceのアクセスキーなんかを全部Regenerateしろ、とか言われそうでかなり憂鬱。。。(--

 (4/9 追記)

例えばAWSのアクセスキーが環境変数に設定されている場合、Herokuルーターのルートからそれが外部に漏洩しなかったとしても、肝心のAWSサーバーに脆弱性があれば、やっぱり再生成が必要なわけで、結局のところアクセスキーの類は全部再生成が必要な気がします。


★その他個人としての対応など

サーバー側の心配もありますが、個人で使用しているアカウント類の心配もした方が良いのかもしれません。脆弱性のあるサーバにログインするとそのユーザー名、パスワードを盗まれている可能性があるわけで。。。

パスワードを変更した方が良いとは思うんですが、今それをやってもそのパスワード変更リクエストが盗まれる可能性があるので、どないせーっちゅーねん。。。(--

とりあえずはベンダーからのアナウンスがあるまではアクションを起こすべきではないんじゃないですかね。。。

おいおいGoogleとかEvernoteとかからパスワードを変更しろっていうメールが届き始める気がしますが、絶対フィッシングとかしかける奴がいる気がするので、ドメインとかちゃんと確認した方が良いですよ。


心の底から思うんですが、誰か間違ってると指摘してください。(--

続きを読む "OpenSSL祭り(CVE-2014-0160)" »

2014年4月 4日 (金)

HerokuのWebSocketでC10Kに挑戦(前篇)

前回SalesforceハッカソンにWebSocketのクイズアプリを出してきたよ~という話をしたわけだが今のところ身内以外からはほとんどアクセスされてないっぽい。
まぁほとんど宣伝してないからそれは別に構わないんだけど、審査されている形跡もないのは大丈夫なのか。。。(^^;


さて、それはさておきWebSocketアプリを作ったら是非試してみたいと思っていたことのひとつにC10K問題の検証と言うのがあります。
C10Kとはクライアント1万台問題の略で平たく言うと「WebSocketってクライアントとずっとソケット繋ぎっぱにするわけだよね。そんなのクライアントの数がちょっと増えたらあっという間に破綻するんじゃね?」という問題のことです。


★検証シナリオ

今回作ったアプリにはルーム毎にチャットの機能があるのでそれを利用することにします。
具体的な目標数値としてはとりあえず以下のように設定しました。

  • ルームを100個用意する
  • 各部屋に3秒に1人ずつ、100人(100クライアント)入室する(最大時同時接続1万クライアント)
  • 各クライアントは5秒に1回チャットメッセージを投げる(合計100万メッセージ)


とりあえず1万クライアント。全員を同じルームにいれちゃうとチャットのブロードキャストがえらいことになってしまうので、ルームだけは分けることにします。

3秒とか5秒とか段階的に負荷を増やしているのはWebSocket接続はだいたい1接続が1人の人間に対応するので、人間の操作としてそこまでの連続リクエストはこないだろうと思われるからです。(先にDevSumiで行ったデモは特殊な例です。(^^;)

この数字をもう少し細かく見ていくと以下のような負荷になります。

  • 最大時秒間2000メッセージ(1万人が5秒に1回メッセージ送信するから)
  • その時のブロードキャストメッセージが20万件(1メッセージはルーム内の100人にブロードキャストされるから)
  • 1クライアントが1秒間に受信するメッセージは20件(ルーム内の100人が5秒に1回メッセージ送信するから)


。。。やりすぎ?。。。

しかし、普段負荷テストをする時はJMeterでぶんぶんリクエスト投げてるわけだから、3秒に1人追加とか5秒に1回のメッセージとかもの凄く手加減してる気分なんだが。。。。(--

それに1クライアントあたり秒間20件のダウンロードがあると言っても、チャットのメッセージなんか1000個あってもそこらのHTMLページのサイズよりも小さいだろうから、通信のコスト自体はどう考えてもhttpアプリのJMeterテストよりも低いはず。
そもそもWebSocketアプリの負荷テストのセオリーがわからないし、テストツールも自作するんだからとりあえずやっとけやっとけ。

Herokuはこの程度の負荷ではおかしくならない!(多分)


★とりあえず1dynoでテスト

いきなり高負荷のテストをしようにもどの位のDynoを用意すれば良いのかまったく見当がつかないのでまずは1dynoの臨界点を見極めることにします。

手始めに

- 1ルームに100人入室、100メッセージ送信

というテストをしてみたところ、何の問題もなく正常終了しました。
まぁNon-Blocking I/Oを使っているんだからこれくらいはやってもらわないと、という感じです。(^^;
ちなみにこの時点でテスト環境で使っているRedisはRedisCloudの無償版です。無料枠でもこの程度のリクエストは捌けるってことですね。

ここから人数とメッセージ数はそのままでルームを増やしていきます。



- 2ルームに100人入室、100メッセージ送信

まだ何のエラーも起こりません。
メモリ使用量も200MB未満なのでかなり優秀です。
Play(というよりもNettyとAkkaのActor)はかなり筋が良いんだろうと感じました。


- 3ルームに100人入室、100メッセージ送信

接続できないクライアントがでてきました。
が。。。サーバー側にエラーログが出ていないところを見るとどうやら限界を迎えたのはクライアント側っぽい。
テストクライアントはjava_websocketを使ったPlayアプリケーションとして作っているんですが、このライブラリが内部的に使っているのは通常のSocketクラスなので、この辺がNon-Blocking I/OとNormal I/Oの差でしょうか。またjava_websocketはSocketの監視のために接続毎にスレッドを作っているのでその辺もメモリ使っちゃって臨界が早い原因になっていると思います。

ここから先はテストクライアントもHeroku上で複数dynoを立ててのテストとなります。テストクライアントをHerokuにあげて実行したらこれもクリアしました。

本当に優秀。。。ていうか予想ではそろそろエラーになるはずなのになんで動いてるの?って感じ。。。(^^;;;


- 4ルームに100人入室、100メッセージ送信

ここにきてようやく2種類のエラーが発生しました。

まずそろそろ来ると思っていたRedisの最大接続数到達エラーがでました。
無料枠のRedisの接続数はたったの10個しかありません。RedisClientはコネクションプールから取るようにしていますが同時接続数が増えると当然並列で使われる件数が増えます。また、それ以上に影響が大きいと思われるのはルームの数だけRedisのSubscriberが必要になる点です。4つのルームがアクティブなら4つのコネクションがそれぞれのルームに占有されます。つまり同時接続数は増えているにも関わらず使えるコネクション数は減っているわけでむしろよくここまで持ったと思います。
本番環境ではRedisGreenを使ってますが、この先のテストでは接続数無制限のRedisCloud(2.5GB)を使うことにします。


またHerokuのRouterがH11を返すようになりました。このエラーの意味はDevCenterのドキュメント、Request queueingのあたりに書いてあります。


HerokuのRouterは1dynoに対して50同時接続しか行わずそれ以降はキューに入ります。そのキューがあふれた場合にこのエラーが発生するわけです。
しかし実際には400近くの接続が1dynoで行われています。これが何でかというとルーター自体が複数台あるからです。結果からの推測では1アプリで少なくとも8台ルーター使ってる計算になります。
なので、H11が発生した場合でもリトライすると別のルーターに繋がってあっさりと接続できたりします。(はっきりと裏を取ったわけではありませんが高負荷時にブラウザからアクセスした際にそれらしき現象を確認しました。)

この動作はWebSocketに限らずhttpアクセスの場合も同じはずです。Httpクライアントからのアクセスがエラーとなった場合に直後にリトライするのって意味あるんだろうか?と思ってましたが、少なくともHerokuでは意味があると言えます。(まぁH11が発生しているなら素直にdynoを増やせっていう話ではありますけど。)


★1dynoの限界

ここまでの結果からH11が発生する400前後が限界となります。
ただしサーバー側のメモリ使用量を見ると300MB前後なので、ルーターさえリクエストを回してくれればもっと多くのリクエストを1dynoで捌けるだろうとは思います。

ちなみに2X dynoの場合でもこのルーターの制限値は変わらないようで、これはもうちょっとなんとかならんのか?あるいはWebSocket有効のルーターでは制限値を引き上げられないか?ということをサポートに問い合わせ中です。

どうやら現在ちょうどルーター周りの改修を行っているらしく、うまくいけば近々対応されるかもしれません。(みたいな返事が来ているが実際のところはどうなのか謎。。。)

この結果を受けて1万クライアントを捌くには25dynoくらいだろうと辺りを付けて実際試してもいるんですが続きはまた今度。
これ書きながら追試したいことやサーバーサイドを直したいところも出てきているので。(Herokuに問い合わせ中のモノもあるし)

ちなみに現在のところ、完全にノーエラーで接続できていると確認できたクライアント数は5600台位です。

傾向としては1度接続が確立したクライアントの動作は安定しておりパフォーマンスもほとんど悪くならないようです。(何故ならソケット繋ぎっぱだから。)

負荷がかかってくると接続時にエラーになることが多いようで、そこをなんとかすれば7,8000まではすぐに行くんじゃないかという気もしてます。(1回のテストで2GB以上のログが出るんで雑な検証しかしてませんが。。。(^^;;;)

後篇がいつになるか(あるいは本当に書かれるのか?)は未定です。(^^;;;

2014年4月 1日 (火)

WebSocketクイズアプリを作りました

約3週間ぶりのエントリですね。(^^;
気が付けば新年度。道理で電車が混んでるわけだ。。。(--

この一ヵ月ずっとSalesforce1 Mobile Hack Challengeに出す用のアプリを作ってました。

んで、作ったアプリがこちら

複数の人が集まってクイズ大会ができるパーティアプリみたいな感じのものですね。(自分の行動範囲の中では)主にIT系の勉強会での使用を想定してます。

コンテスト的にはSalesforceを使ってないという時点でもうアウトなんじゃないかという気がしなくもありませんけど。。。(^^;

コンテストの結果はともかく、このアプリの最大のテーマは、

業務アプリの作成ではまだまだリスクがありそうに感じる最新技術を使い倒す

ことだったので、それはそれで価値のある仕事だったんじゃないかと思います。
もちろん、このアプリがどこぞの勉強会で使われるのであればそれはとても嬉しいことです。

★WebSocketアプリの可能性の話

今回クイズアプリとして「出題者 vs. 参加者」というモデルのアプリケーションを開発したわけですが、個人的にはこうした「ルームマスター vs. 参加者」というモデルのアプリケーションが存在するということに気が付いたことが最大の収穫でした。

これまでずっと「WebSocket、理屈はわかるけどどういうところで使うんだろ?ゲームとかか?」みたいなことを思ってたんですが大間違いでしたね。

そんな風に思うのは既存のhttpの枠組みでのWebアプリに慣らされすぎているための視野狭窄だったと今にして思います。無意識のうちにこれまで存在したアプリの中でどこにWebSocketを適用するのが有効なんだろう?という方向に思考が制限されていたわけですね。

WebSocketの例としてよくチャットが引き合いに出されることもその視野狭窄推進に一役買っていると思っていて、これなんかも結局は「今までこうだったものが、こう変わります。」という説明でしかないわけです。そしてその説明が納得感の高い物であるが故にそこから先に進むことが難しくなっていた気がします。

WebSocketを考えるのであれば、むしろ今まで世の中に存在しなかったモデルのアプリケーションを作る、という方向で考えた方が良いと思います。
多分そこには宝が眠っています。(^^;


★クライアントサイド技術要素の話

このアプリで使用しているクライアントサイドの技術要素にはだいたい以下のモノがあります。

  • WebSocket
  • SessionStorage
  • PushState
  • CSS3 Animation


本当はあとWebWorkerを使ってみたかったんですが使いどころを思いつかなかった。(^^;

ひとつひとつは既に日本語での詳細解説ページも多数あるので目新しい物ではないですが、お試し程度に使っているのではなくちゃんと一つのアプリを作るために協調的に使われているというのがミソです。

例えば

  • Ajaxは一切使わずに通信は全部WebSocketで行っている
  • テンプレートは全部SessionStorageに入れて使いまわしている


というようなことをやっているんですが、このあたりは理屈ではできると思ってもなかなか実案件には適用しにくいモノなんじゃないかと想像します。(一昔前のAjaxがそうであったように)

実際のところ、これらのことをアプリ開発の度に作りこむのは辛すぎるので何かしらのフレームワーク的なモノを使いたくなるんですが、現状ではそうしたライブラリもほとんどないので全部自作です。(^^;;;

次回以降こうした部分についてのノウハウを書いていきたいと思います。

乞うご期待!(全然関係ないですが今日は4月1日ですね)

2014年3月12日 (水)

CSS3アニメのjQueryプラグインを作ってみた

今作っているアプリでちょっとしたギミックをいれるのにCSS3のanimation-xxxxを使っているんですが、これがすこぶる便利です。(^^v

CSS3アニメについてはこちらのページが良くまとまっています。

 

http://ri-mode.com/rainbow/2013/06/10/css3_keyframes_transform/

 

ていうか僕はこのページ以外のリソースにはほとんど目を通してないんですが。。。(^^;;;

それでも、なんとなくエフェクトが作れちゃいます。

アニメーションの作成などまったくの門外漢で、やろうと思ったこともほとんどないんですが多分、

  • 開始(0%)と終了(100%)の状態を定義して、
  • 中間(n%)でどういう状態を経過するかを定義するだけ

という仕様がプログラマ脳と相性が良いんだと思います。

また対象要素を最初hiddenにしておけば、showした瞬間にアニメーションが始まるのもスクリプトとの相性が良くてGoodです。

さて、このCSS3アニメ、素のままでもそれなりに使えますが3つ4つと定義していくとやっぱりだんだん面倒になってくるので既存のコードを切り出してjQueryプラグイン化してしまいました。

 

http://shunjikonishi.github.io/jquery-animateDialog/

 

制作は全部で3時間くらい。GitHub IOのおかげでソースだけでなくホームページまでこんな短時間で準備できてしまうのは素晴らしすぎます!

使い方は多分、上記ページを見れば(僕の適当英語でも)わかると思います。

サンプルはテキストオンリーのシンプルな要素を使ってますが、もちろんイメージ等を駆使した凝った要素であってもアニメーションさせることができます。

自作のアニメーションを作るのもkeyframeを定義するだけなので、職人の手にかかればかなり凝ったものを作ることもできると思います。

いけてるアニメーションを作った方はプルリクくれればマージしますよ。(^^;

(適当英語の校正も歓迎です。)

2014年3月 5日 (水)

JavaScriptのWebSocket API

ブラウザのJavaScriptはシングルスレッドで動いている。

そんなことは知っている。

いや、知ってはいてもちょいちょい忘れてていけてないコードを書いちゃうこともあるのだ。

JavaScriptでのWebSocketの使い方は以下のようなコードになる

 

var ws = new WebSocket("ws:...");
ws.onopen = function(e) { ...};
ws.onmessage = function(e) { ...};

 

このコードを見て「onopenをバインドするよりも前に接続が確立しちゃったらどうなるんだろう?」と疑問に思わないだろうか?

でも、実際にはそんなことは起こらない。何故ならWebSocketの接続が行われるのはコンストラクタを実行している関数の処理を抜けた後だから。

このことは以下のようなコードで検証できる。

 

function init() {
    console.log("Start init: " + new Date().getTime());
    var ws = new WebSocket("ws:...");
    ws.onopen = function(e) {
        console.log("WebSocket open: " + new Date().getTime());
    }
    for (var i=0; i<1000000; i++) {
        var dummy = new Date();
    }
    console.log("End init: " + new Date().getTime())
}

 

空ループを回さなければ20ms以下で接続が確立するが、このコードではループが終了するまで接続は行われないので数秒のタイムラグがある。

言い換えれば「End init」のログは常に「WebSocket open」よりも先に出力される

(ただしFirefoxでFireBugを動かしていると途中で「スクリプトが応答していません。」のようなダイアログが表示されることがあり、その場合はそこに割り込んでWebSocketの接続が行われる。)

何が言いたいかと言うと、

 

function init() {
    var ws = new WebSocket("ws:...");
    //なんか重たい処理
    ws.send("hoge");
}

 

のようなコードは途中にどれだけ重たい処理があっても必ず失敗するのでちゃんとonopenを使おうという話でした。

2014年3月 3日 (月)

SessionStorageのスコープ

先週のHTML5カンファレンスで@albatrosaryさんのセッションを聞いて自分がSessionStorageのスコープについて誤解していることに気が付いたのでちゃんと調べました。

SessionStorageのスコープってウィンドウ + ドメインなんですね。。。
半端知識でCookieと同じだろうと思ってたら全然違いました。。。(--

 

★誤解その1、同一ブラウザでもウィンドウ(あるいはタブ)が違うとスコープが異なる

同一ブラウザ上でタブを二つ開いて、両方で同じページを開いた場合それぞれのSessionStorageは別になり値は共有されません。

Cookieやサーバーサイドセッションでは同一ブラウザ、別ウィンドウは区別できないのでこれはSessionStorageだけが持つ大きな特徴と言えます。

 

★誤解その2、パスがちがってもドメインが同じであればスコープも同じ

ウィンドウが同じであれば「http://SERVERNAME/test1」と「http://SERVERNAME/test2」のSessionStorageは値を共有します。

Cookieの場合はPathでスコープを制限できますがSessionStorageにはそのような機能はないので同一ドメインであれば常に同じスコープになります。

ちなみにプロトコルが異なる場合(httpとhttps)は別スコープになります。

 

★疑問その1、別のサイトに移動して戻ってきた場合はどうなるの?

対象のページから全く無関係なページ(例えばyahoo.co.jp)に移動して、また同じページ(あるいは対象ページとドメインが同じな別ページ)に戻ってきても、SessionStorageの値はクリアされず維持されます。

要するにウィンドウを閉じない限りSessionStorageの値も残り続けるということです。

 

★疑問その2、モバイル端末がスリープした場合どうなるの?

ウィンドウを閉じさえしなければスリープしたりブラウザから別のアプリに移動した場合でも値は維持されます。

動作確認したのはAndroid 4.1.2(標準ブラウザ、Chrome)とiPad2です。

ちなみにモバイル以外はIE10 IE11, Chrome, Firefoxで動作確認しましたがすべて同じ動きになりました。

 

□□□□

なんというか思ったより都合の良い動作しているのでSinglePageAppを作る場合には割と使い勝手が良さそうです。(^^;

2014年3月 2日 (日)

Enterprise × HTML5 Web Application Conference 2014

行ってきました。
予想以上に自分の興味分野とのマッチ度のかなり高いセミナーで非常に面白かったです。

会場は明星大学。つまり大学の教室での講演だったので机椅子完備なのはありがたかったです。
ていうか、そこで朝から7コマもセミナー聞いているのは完全に授業でしたね。(^^;

以下興味深かった内容のまとめです。


★AngularJSの魅力

AngularJS入門セッション
個人的に以前からAngularJSはイマイチ好きになれないなぁと思ってたんですが、このセッションを聞いてようやく理由がわかりました。

JavaScriptを1行も書かなくて動く各種機能が気持ち悪い

と、思っちゃうせいですね。(^^;
よくよく考えると単純なタブ切り替えの場合なんかでもBootstrap(スクリプト不要)よりもjQuery-UI($().tabの実行が必要)の方が好みです。

スクリプトを見て「ここでタブ切り替えやってるんだな」とわかる方が好ましいと思うんですよね。
スクリプトの場合ライブラリの細かい仕様を知らなくても、多分この辺でやってるんだろうなぁとある程度想像がつきますが、HTML内で完結されると知らないと絶対にわからないですからね。

自由度が高くないという特徴もこれに起因するものだと思っていて、HTML内に専用のタグを埋めるにしても最終的にはスクリプトでそれを制御する形の方が扱いやすいんじゃないかと思います。
まぁ、僕がJavaScript好きなせいかもしれませんが(^^;

YeomanとBower(JavaScriptのパッケージマネージャ的なもの)は最近急によく見かけるようになったキーワードだったので、さらっとでも紹介を聞けたのは良かったです。
近いうちに試します。


★HTML5セキュリティ入門

xhr2、WebSocket、WebStorageなどを使用する際に気をつけるべきセキュリティの話
これははっきり言って資料が完璧です。

http://www.slideshare.net/muneakinishimura/webhtml5-31749532

フロントエンド開発者は読んでおくべきと思います。

プロトコルによるoriginの付き方やブラウザの挙動の違いなど非常に実践的てためになる話でした。


★オフラインアプリケーション開発入門

SPA(Single Page Application)の話とその発展系としてのオフラインアプリケーションの話

http://www.slideshare.net/sagawafumio/ss-31750106

SPAについてはちょっともの申したいこともあるのでまたあとで。
オフラインアプリケーションについてはWebアプリでどの程度必要性があるんだろうという疑問がありますが、WebStorageは意外と応用範囲の広い技術かもしれないとは思いました。

例えばセールスマンの使うカタログアプリケーションで原則アイテムはネット上にあるけど、おすすめ商品だけは選択的にローカルに落としておくとか。

SPAならそれこそテンプレートを全部ローカルストレージに持たせるのもアリかと思います。


★「Selenium」を使った、開発・テストの効率化

Seleniumを使った自動テストの話
講師の伊藤さんとは懇親会でもかなりお話させていただきました。楽しかったです。
Selenium。そんなに使いこんでるわけではないけど、どうやら自分の知らない便利機能とかはあまりないっぽい。
もうちょっと面倒くさくなければ、嬉しいんだけどね。。。(--

今度作るアプリでは細かいテストをたくさん作るのではなく、長めのシナリオを少数作ってみようかなと思ったり。


JavaエンタープライズアーキテクチャにおけるHTML5

Javaユーザーグループの鈴木さんよりSPA、適用できる部分もあるけどまだまだエンタープライズでの利用にはリスクもあるよ、という話。

http://www.slideshare.net/yusuke/javahtml5

何というかここまで割と空飛んでる感じの人が多かったのに最後にしっかりと地に足の着いた人がでてきて、そのギャップが面白かったです。

どうでも良いけど古いIEなどHTML5非互換のブラウザ対応の問題を指して「ただしイケメンに限る」問題と名付けたセンスが秀逸すぎます。(^^;
うん、それはどうしようもないわ。それはあきらめるしかないですね。(^^;


★番外 Vert.xの話

セミナーではありませんが、懇親会でVert.xの話が聞けたのも大きな収穫でした。
WebSocketをスケールさせるのであればVert.xが最強らしいです。

Vert.x。名前は時々見かけてたんですが、さらっと調べた感じ結構面白そうなフレームワークでもあるのでこれも近いうちに評価したいです。

家に帰ってから気が付きましたが、隣でSenchaの話をされてた方だったんですね。

http://www.slideshare.net/kotsutsumi/html5-31768731

そうと知っていればjQueryMobileとSenchaの比較をもっと聞いてみたかった。。。(--


★SPAの話

さて、今回大きなテーマのひとつだったSPA。
「エンタープライズ」という枕がついていたせいか思った以上に経験者が少なかった印象です。


SPAという単語自体がでてきたのは極最近だと思いますが、エンタープライズはともかくサービス提供者は多かれ少なかれSingle Page ArchitectureでWebアプリを作っていると思います。


(余談ですがSPAのAは、個人的にはApplicationよりもArchitectureと言った方がしっくり来ます。「Application」と言うとすべての機能がひとつのページで実現されなければならない印象ですが、別にそんなことはなくて複数の機能が画面遷移なしで実現されていればそれはSPAと言っても良いと思うのです。。。)

僕自身は最初にSPAモデルでアプリを作ったのは3年以上前ですし、それ以降自分でWebアプリを作る場合は常にSPAモデルを採用します。

1年くらい前には「お手軽Ajaxアプリケーションの作り方」というスライドも書きました。(今見ると直したいところがいっぱいありますけど。。。)

http://www.slideshare.net/shunjikonishi/ajax-app-20725783

個人的にはある程度JavaScriptが書ける開発者であれば通常の画面遷移があるアプリケーションを作るよりもSPAモデルで作った方が絶対に簡単だろうと思います。


だって画面遷移したら毎回ステート切れて必要な情報は都度サーバから取ってこなきゃならないじゃないですか。慣れてるからそれが普通に思えるのかもしれませんが、本来それはとても面倒なことです。

単純な例で

  • フォームで情報を入力
  • 確認画面表示(間違いがあれば入力画面に戻る)
  • 登録


という画面を画面遷移モデルとSPAモデルで実装することを考えてみてください。
やったことなくてもSPAモデルの方が簡単そうに思えるんじゃないですかね?(これをSPAというかどうかはさておき)

このように本質的にはSPAモデルの方がアプリ開発は簡単になると思っているんですが、現状SPA特有の難しさがあることも事実で複数人で開発する案件に適用してもあまりうまく行くような気がしません。

これは何でだろうということをこの週末ずっと考えていたんですが、とりあえず

JavaScriptのコードを局所化することが難しいため

という結論に達しました。
グローバル変数は悪、というのは広く浸透した知識だと思いますがHTML内のidやclassは全部グローバル変数みたいなもんですからね。この課題をどうにかしないといずれは複雑さが理解の限界を超えて破綻するように思えるのです。

ていうかこれ破綻したときのダメージが再起不能レベルのような。。。(--

AngularやBackboneなどのフレームワークを使うのも有効な手段だとは思いますが、課題の存在に気づかず漠然と使っているだけだといずれは同じ結末を迎えるように思えます。

対処法はいくつかあるとは思うんですが、それらは整理できたらまたおいおい書きたいと思います。

とりあえず先週書いたこれも有効な対策の一つではあるかと思います。(^^;

良質なコードを高速に書くコツ
http://www.slideshare.net/shunjikonishi/ss-31700673

SPAを意識して書いたものではないですが、こういうJavaScriptの書き方に慣れておかないと多分SPAはつらいです。(^^;

採用情報

株式会社フレクトでは、事業拡大のため、
Salesforce/Force.comのアプリケーション
開発
HerokuやAWSなどのクラウドプラッ
トフォーム上でのWebアプリケーション開発

エンジニア、マネージャーを募集中です。

未経験でも、これからクラウドをやってみた
い方、是非ご応募下さい。

フレクト採用ページへ

会社紹介

株式会社フレクトは、
認定コンサルタント
認定上級デベロッパー
認定デベロッパー
が在籍している、
セールスフォースパートナーです。
heroku partnersにも登録されています。
herokuパートナー
株式会社フレクトのSalesforce/Force.com
導入支援サービス
弊社の認定プロフェッショナルが支援致します。
・Visualforce/Apexによるアプリ開発
・Salesforceと連携するWebアプリ開発
も承っております。
セールスフォースご検討の際は、
お気軽にお問合せください。
Powered by Six Apart