2ch CGI 軽量化

2ch のユーザ数はどんどん増え続け,それに伴いサーバ負荷も増大しています.その対応のため, root ★ 氏や FOX ★ 氏などによってハード/ソフト両面からサーバ環境の強化・改良が日々行われています. ここでは,その内主にソフト面,特に CGI の軽量化について説明します.

2ch で頻繁に呼び出される CGI は,レス書き込みの際に使用される bbs.cgi, IE / Mozilla 等一般ブラウザでスレッド閲覧のために使用される read.cgi です. これらは以前普通の CGI が用いられていましたが,その起動負荷がサーバにとって 大きな負担となっていました.そうした起動負荷を低下させる仕組みを導入したところ, bbs.cgi / read.cgi ともにかなりの効果を上げることができました.

bbs.cgi = Perl CGI → SpeedyCGI

Perl CGI の軽量化の方法としては,例えば mod_perl がありますが,bbs.cgi は mod_perl での使用に耐えるほどクリーンなコードでなかったためか, 正常に動作させられませんでした.そこで,別の方法として SpeedyCGI が用いられました. SpeedyCGI では Perl スクリプト実行のため httpd とは別のプロセスが 起動されますが,普通の CGI のようにリクエストごとに起動されるのではなく, Perl 実行プロセスはバックエンドとして常駐し,フロントエンド (speedy プロセス,あるいは httpd に組み込まれた mod_speedycgi) からの呼び出しを待ち受けます.これにより,fork(), exec() といった 起動負荷が大幅に軽減されます.

さらに,Perl 実行プロセスが常駐するため,いったん読み込んだデータなどを キープし続けることができます.これを利用して,設定情報や ID 生成の種などを キープし続けるようにし,ファイル読み込みによる I/O 負荷の軽減も図られました.

read.cgi = C (バイナリ) CGI → DSO

C (バイナリ) CGI の軽量化の方法としては,そのまま Apache モジュール化すると いうことも少なくないかと思われます.しかし,read.cgi においては mod_cgidso が用いられました.これの概要についてはリンク先を参照して下さい.

# DSO = Dynamic Shared Object, (動的)共有オブジェクト

mod_cgidso は最初は一部のサーバで実験的に導入されたのですが, その際の read.cgi は以前オープンソースで 改良作業が行われていた当時の古いソースをベースに mod_cgidso 用に書き換えたものを用いていました.全サーバへの導入に当たっては (その時点での)現行版 read.cgi を mod_cgidso に対応させる必要がありましたが, FOX ★ 氏により一から全面的に書き直した read.cgi が作られ, それが現在用いられています.

read.html = CGI 不使用 → JavaScript

JavaScript を用いて dat ファイルを直接取得し整形して表示することで,これまで read.cgi が担っていたサーバ側の処理を不要にしようという実験が進められています. さらに,従来の read.cgi になかったいくつかの機能も JavaScript を活用して実装します.

とはいえ,一部サーバ側で実行される処理もあります.dat ファイルの取得には XMLHttpRequest を用いますが, Shift JIS のデータについて Content-Type ヘッダで charset=Shift_JIS の指定を行えば Firefox, Opera や IE 7 等では問題なく扱えます.しかし,IE 6 では初回取得時は 正常に扱えるものの,次回以降サーバから "Not Modified" のレスポンスを受けた際, ローカルキャッシュのデータを UTF-8 として扱って文字化けする問題があるため,IE 6 の場合だけ mod_charset_lite を用いて UTF-8 へ変換した dat を取得するようにしています.また,ライブな dat が存在しない場合に ErrorDocument ディレクティブによる指定で,過去ログの有無を調べる DSO プログラムを呼び出すようにし, 過去ログがあれば従来の read.cgi 同様に最初と最後のレスのみ抜き出し返します.

現在,「また挑戦。」板で実験的に稼働中です. ただし,JavaScript はブラウザによる実装や挙動の差異が少なくないため,実用化段階では マイナーなブラウザや古いブラウザは従来の read.cgi を利用してもらうことする方針です. なお,対応ブラウザでも大きなスレッドを全部一度に表示すると重くて時間がかかるので 注意して下さい(それを避けるには部分表示を用いるようにして下さい).

雪だるま作戦と matd & bbsd

従来,2ch では個別のサーバ単位で処理を完結させていましたが, 複数のサーバで処理を分散化して処理能力を高めるための 仕組み作りが「雪だるま作戦」として進められ,それがついに 稼働し始めました.

bbs.cgi の処理のうち入力チェックや規制処理などを フロントエンド側で行い,チェックをパスしたリクエストを バックエンド側に渡してファイル書き込みなどを行います. そして,フロントエンド側で HTML / dat ファイル読み出しや read.cgi 経由での閲覧のリクエストを受けると, バックエンド側からファイルを読み出しクライアントに返します. このファイル読み出しには,リアルタイム性が要求されるものは HTTP proxy, そうでないものは rsync を用いています. フロントエンドを複数配置することで負荷分散も図られます. また,フロントエンドでは Squid を用いて dat ファイル等をキャッシュします.

matd によってそれらの複数のフロントエンドはロードバランスされています. また,ロードバランサも2台配置し UCARP による冗長化を行っています.カーネルデバイス版の CARP を用いないのは, 物理 I/F とは別に carpN という仮想 I/F を作成するとか,物理 I/F を promiscuous モードにしてしまうとか,そういった点で matd との相性が悪いためです.

そして,バックエンド側でファイル書き込みなどを行うデーモンとして bbsd が用いられています.この bbsd はフロントエンド側の bbs.cgi から UDP ソケット経由でリクエストを受け取ります.また,従来の bbs.cgi においては,スレッドタイトルを管理する subject.txt ファイルの更新処理が大きなボトルネックの1つとなっていたことから, bbsd ではこれをオンメモリで管理し,ファイルへの書き込みは 一定のインターバルを置いて行うなど軽量化も図られています. さらに,ID 生成の種や投稿規制のためのデータなど, リアルタイムにデータを更新しかつ複数のフロントエンド間で 同一データを共有しなければならないものを登録・チェックする 機能も実装しています.

その他の取り組み

上記のような CGI 軽量化以外にも,負荷対策の取り組みは様々に行われています.

一部のユーザによる過剰なクロールやリロードがサーバに大きな負荷をかけているため, それを弾く仕組みが「バーボン」の強化版として導入されました.一方,現在は dat キャッシュサーバ "Black Goat" というのが携帯サーバ用に内部向けのみに公開されているのですが, 外部向けに "White Goat" というキャッシュサーバも近々公開される予定です.

それ以外に,現時点では 2ch では実施されていませんが CGI 出力キャッシュで述べているように,mod_mem_cache による 動的コンテンツのキャッシュは負荷軽減にはかなり効果的です.

関連スレ