Railsで中規模システムを作ってみたので、共有してみる。
作ったシステムはクラウド・・・というかASPというか。そんな感じのもの。
ターゲットは月間200万PVくらいのお客さんを想定したシステム。

使ったテクノロジーと困った点とかを書いてみる。
使ったのはRails 2.3.5。これを製作中にRails3が正式リリースされたので残念ながら3系は見送り。いずれバージョンアップするかもしれないけれど・・・。
MySQLは5.5系だけど、正直この辺りはどうでも良い。5.5系でinnodbプラグインが入ったのでバージョンアップしたがDBに関しては特になんでも良かった。この辺りはRailsの凄さだな。

で、開発環境はmongrelで本番環境はapache2.2+passenger。まぁ、基本構成かな。
ただし、passengerはviewにコメントを入れると誤動作を起こす事があったりmongrelと微妙に違う動きをするので結局vmをたてて開発環境でもcentis5+apache2.2+passenger構成を作った。
Linuxでしか動かない場合や、本番と挙動が違う時はvmとても便利。僕の隣の人はWindowsを見限ってubuntuを入れて楽しんでた。なんかmacみたいで楽しそうだった。

ちなみに困った点としてpassengerのメモリ消費量。僕が思った以上に食う食う。ASPサービスなのでメモリ消費量が多いとちと困る。という事でunicronを使ってみたけど・・・これも同じくらいのメモリを食う。
ASPのサービス上一つのお客さんに割り当てるプロセス数が限られるという制約が無ければunicronも良いと思うんだけど。。。(unicronは一つのサービスに大して複数のスレッドを起動出来る。passengerはスレッドが使えないのでプロセスを並列に並べるしか出来ない。本当にトラフィックが多いお客さんの場合はunicronは有効。ただし、特にそんな要件が無いならデフォルトの設定がほぼベストの状態であるpassengerがおすすめ。)

railsの開発はキャッシュが基本という事なので、何度も呼び出しがあるものに関しては
def test
@test ||= Test.find(:all)
end
というようなインスタンスキャッシュをあちらこちらに書くのが基本となった。
Railsには標準でSQLキャッシュが付いているが、なにげにキャッシュを呼びにいくまでのコードが長い。なので、極力インスタンスのキャッシュを使って速度を保つ。
また、each等でまわしつつhas_manyを使う場合はincludeが基本。
class Roop
has_many :sub
end

-----------------

roop = Roop.find(:all)
roop.each { |r|
r.sub.each { |s|
# 何かの処理
}
}
なんてコードがあるなら
roop = Roop.find(:all, :include => :sub)
は基本。このincludeはネストして使用する事が出来るので
roop = Roop.find(:all, :include => {:sub => :subsub})
なんてやる事も可能。
とにかくキャッシュをふんだんに使う。rubyでは明示的にstatic領域が使えないがTESTや@@testなどのコンストやクラス変数を使えばstaticが使えるので多いに活用させる。
こうする事によって、超情報量の多いTOP画面を

秒間4アクセスで0.3秒レスポンス

を実現。passengerのプロセス数を増やしてみたら秒間100アクセスでも問題なくさばけた。
とにかくRailsでは無駄な処理を省いて同じアクセスは全てキャッシュで返すという事を全体リクエストでも1リクエストでも行う方が良い。僕は「railsのキャッシュ技法」って本が書けるくらいキャッシュを上手く使った気がする。
(メモリとトレードオフになる場合もあるので、そこは注意。キャッシュして大切なメモリを失ってしまうと元も子もない。)

後はファイル管理の部分。ファイルの管理はどこも頭を悩ませるが、基本的にはファイル情報をDBで保持させて実態はapacheに参照させるのが王道。
しかし、冗長化の構成の場合はそうは行かない。NFS等で同期をとるかファイルサーバーを用意する必要がある。
NFSはマスタ(という表現が正しいか分からないが)が落ちてしまうと、ファイルをとりにいくプロセスが張り付きになるという危険を秘めているため今回は見送り。(これは突き詰めるとログを監視すれば回避出来るが、逆に言うとそれ以外の方法では回避出来ない。)
なので今回はファイルサーバーを作る方向で対応。その時に使ったのがwebdav。wevdavはhttpプロトコルだしrailsに標準でメソッドが用意されていたので実装は1日もかからずに終わった。
しかも、イメージのアクセスが全てファイルサーバーに行くのでアプリの負荷が低いという良い事づくし。(ファイルサーバーが増えるという欠点はあるが。。。)
railsと相性が良いと思うので、冗長化する際には是非検討してみてください。

検索は基本はDBだが、キーワード検索(like検索)等はsolrを使用。rubyにはrsolrがあるのでhttpリクエストは自動的に作ってくれる。solrではキーワード管理もそうだけど、第三正規化してると遅くて仕方が無いデータ抽出しているところでも活躍。すなわち、マスタデータは第三正規化で保持しておきインデックスを第一正規化で作るような感じ。
で、2グラム検索。senも入れてみたのだが要件に添えないのと精度がいまいちなのと開発が終わってるっぽかったのでキーワードでのインデックスは作らなかった。
ってか、solrでキーワードインデックスを作ってる人って辞書変更によるインデックス再構築をどうしてるんだろう?という疑問がわいた。。。ので、この辺りは2グラムを駆使して華麗にスルー。

i18nは今回未使用。その理由はASPのためキーワード等のカスタマイズをお客さんがやりたい場合があるため。
いやー。この恩恵がないのはちょっときつかった・・・。ここは手作りi18nで泥臭く対応。
しかし、この部分は色々工夫を凝らせて楽しかった。意外と手作りでも行けるのと、手作りだと融通が利く。

SQLはプログラム内部のリテラル禁止でやった。railsはsqlを分けるレイヤーが無いのでtownsoftプラグインという僕の屋号のプログラムで対応。
これは僕がrails開発を行う時に楽になると思う機能を適当に入れてるプラグインだ。欲しい人が居たらあげます。
で、これを使ってSQLファイルを分離。SQLファイルは分離したがモデルからfind系のメソッドと同じように呼べるようにしている。
これでコードとSQLの分離をはたして可読性があがった。(これはなぜRailsには無い機能なのだろうか・・・。僕はこのプラグインを相当重宝してるが。。。)

モバイル対応はjpmobileを使って簡単に出来た。このプラグインのfind_templateメソッドのhookメソッドは一度見ておいた方が良いかもしれない。
僕はこのコードを参考にテンプレート呼び出し部分にある仕掛けをして、テンプレートをアプリから変更出来るようにした。(rubyはクラスやモジュールのhookが当たり前に出来るので、頑張って継承やポリモルフィズムを使う必要がないのだ。この辺りはとっても楽だしコードの可読性が高い。)

sslについては何も言う事無いかな。ちょっと特殊な事をしているが、別に特筆すべき事は無い。
(証明書を安くするためには工夫が必要・・・。)

あ~。あと、ハマったのがrufus-scheduler。
こいつはアプリに付いてるスケジューラーなのだが、、、Rubyには決定的なバッチサーバーが無い。
基本的にはcronで対応だが、cronはASPとは相性が悪いしアプリで管理がしにくくなる。(アプリでcronを管理するスケジューラーもあるのでASPとかで無ければそれを使うと良いと思う。僕の場合はそうは行かなかったが。)
何が問題かというとrufus-schedulerはpassengerと共に生きてるので、、、300秒間アクセスが無ければ死んでしまうのだ。すなわち、一緒にrufus-schedulerも動かなくなるのだ。(設定で勝手に死なないようにも出来るが、ASPなので他のサービスにプロセスをとられる事も考えると万全ではない。)
こうなると、スケジューラーは効果をなくす。夜間バッチが動かなくなるのだ。
これはrufus-schedulerとdelayed_jobを組み合わせる事によって解決。(もともとdelayed_jobは使ってたし。。。)

で、そのdelayed_jobでもハマった。こいつは普通の使い方をしていれば良いのだが起動時にアプリのSQLが走ると起動しなくなる。
モデルのクラスメソッドにSQLを書くだけで動かなくなるのだ。(通常は無いが、has_manyの条件をSQLでとりにいったりすると動かなくなる。)


という事で使ったテクノロジーをまとめると・・・
ruby on rails 2.3.5
passenger
solr
delayed_job
rufus-scheduler
jpmobile
webdav
open_flush_chart
townsoftプラグイン
ssl
etc...

とかを使ってrailsで中規模サイトを作ってみました。
構成で困ってる人が忌ましたら、是非情報共有しましょう♩
(早くrails3もがっつり触りたいなー。でも、今のところrails3のメリットがそこまで無いな。KVSとかを使ってるところは恩恵があるのかな。)