masuidriveの人から、ご意見聞かせて、と連絡が来たので、がんばって書いてみます。
masuidrive on rails – Webでの非同期処理を考えてみる [長い記事だけどコメント求む!]
Life is beautiful: マルチスレッド・プログラミングの落とし穴、その2
要するに、重くなってきたサービスに対して、むやみにサーバを増やさないでどうやってパフォーマンスを改善しましょう?全部の機能を同期的に処理する必要はないんじゃね?という話だと理解しました。
ボク程度の人の意見が必要というよりは、何かしら「あっ!」って気づくきっかけが欲しいのだろうから、遠慮せずにだらだらと。
「その程度のことはわかっとるわいっ!」という部分が多いと思いますが、そこはご容赦ください。
全部を非同期にすると…
中島さんの記事で
アクセス数が上がると、ユーザーがした投稿がデータベースに反映されるまでの時間がかかるようになるが、それが直接的におもてなしの低下に繋がることはない
と書いてあって「え~っ?」とまず思いました。
最近ブログのコメント欄なんかで、(スパムを防ぐために)管理者承認後に反映されます、というブログが増えているのですが、これは明らかに、おもてなしの低下につながっています。
単純な話で、自分が投稿したコメントがどう見えるかすぐに確認できない分、ユーザーさんはできあがりイメージを確認せずにその場を離れないといけないわけで。その場でできあがりを確認してから離れられるのに比べて不便なのは確かでしょう。
masuidriveの人の記事を見ると、「でも全部を非同期にする訳にはいかないし」って言われているので、そんなことは承知の上で、人間以外には非同期でいいんじゃね?という意図なのだとは思いますが。
あれ。でも全部非同期って、意外とありかもしれない。だって現にブログのコメントが非同期に反映されることについては、ユーザーの非難囂々という状態にはなっていなくて、それなりにみんなの同意が得られつつあるように見えます。
それだったら、投稿とか削除についても、「ちょっと時間がたってから反映されます」と一言表示されるだけで意外と許されるのかも。
エラーがあったらどうするか。photoshareの場合、幸い(?)ユーザー登録しないとコメントできないシステムみたいですので、
#iPhone持ってないので、詳細は知りませんw
それだったら、エラーになったらメールする、でもいいんじゃないでしょうか。
あと、よくしらんのですが、iPhone上のサービスって独自の(WEBじゃない)アプリが動いてるんですよね?であればそっちに表示させるとか。
同期と非同期をわける方法
ただ、そうやって非同期にすることが一般化すると何が起きるか。
なんだかんだいったって同期でスムーズに動いた方が嬉しいのは事実なので、そうすると、ユーザー数の少ない小規模サービス(技術者が片手間で作ったみたいなの)と、
リソースに余裕のある超大手サービス(YahooとかGoogleみたいな大手がはじめる新サービス)は同期で動いて、
photoshareのような、お金とサービスのバランスをとらないといけないところだけが非同期で機能を提供することになります。
これは痛い。競合に提供できるサービスを自分たちは提供できません、ということになるんだから、これは厳しい。
そうすると、やっぱり全面非同期化は厳しいんじゃないかな、と。
リアルタイムの情報を必要としないRSSとか、クローラーとかには非同期で返しつつ、ユーザーさんの要求はリアルタイムで返す必要があるんじゃないのかなぁ。
じゃあ、同期と非同期をどうやってわけるか。
コントローラごとに実行するmongrelを振り分けるようなルールをリバースプロキシに書けばある程度回避できますが、どのように振り分けるのか決定するのは困難です。(Feedなら簡単だけどもっと難しいケースもあるよね)
このように実はアクションごとに優先順位があります。Feedやランキングと言った部分は、反映が遅くてもほとんど問題になりませんが、自分の写真リストや写真そのものはすぐにアクセスできるようにしたいのです。
現在のRailsはコントローラごとに優先順位を付けるような処理は苦手なので、これを行うには、なんらかの仕組みが必要になると思います。
問題は二パターンあって。
1.create,update,delete系の要求以外を別サーバに振り分けたら問題が解決する場合
2.それでは負荷対策として不十分で、create,update,delete系であっても一部は非同期にする必要がある場合
1.だったら問題は簡単で、railsで動作する同期動作サーバと、rsyncか何かで同期サーバの情報をミラーして提供する非同期サーバを用意して、リバースプロキシ部分の振り分けをがんばればいい話です。
#十分難しいのですけどw まあお二人ならきっとできるでしょう。
問題は2.の時で。アーキテクチャはこんな感じかなぁ。非同期でいい機能はWEBサービスとして切り出して、フロントサーバはバックエンドサーバに投げちゃう。
#当たり前ですけど、本当にサーバを4台以上用意せよ、という話ではなくて。物理的に同じサーバ上でフロントとバックエンドの機能が動いていたってかまわないです。負荷が耐えられるんだったら、ですけど。
バックエンドサーバに要求される機能はこんな感じ。
・要求を受け付けて、キューに入れる
・応答は、静的に生成してあるHTMLかなにかをそのまま返しちゃう
・適宜キューにある処理を実施して、静的HTMLをアップデートする
railsで実現するとすると、
・要求を受け付けたら、それをDBに入れて、生成済みのHTMLを返す。ここはrailsで普通に作れるはず。
・キューにある処理を実施する部分は…
キューの処理は、rakeタスクをcronとかで叩くようなぬるい処理で間に合うんだったらこんな苦労していないから、やっぱりぶんぶん回るサーバプロセスが必要になりますね。
ここでrailsいらないじゃん、という話になるわけか。
いかん。masuidriveの人と同じ結論になってしまった^^;
でも、ここまでをふまえると、やっぱり独自で作るのがいい、という話になると思います。要求とは無関係にぶんぶん回っている時点でもはやWEBサーバじゃないですから、それに適したアーキテクチャのものを書く必要があるんじゃないかなぁ。
なんだかまとまらん話ですが、思うがままに書いてみました。何かしらヒントになるような部分があれば幸いです。
あ。rakeタスクとして作っておきながら、一度立ち上がったら落ちないでぶんぶん回り続けるというのはちょっとステキかも。