Honey Bot

Diary

- チャット暫定版設置完了orz

この1週間いろいろあった…。
ひたすらJavaScriptと格闘しながらさまざまな問題と戦いにつぐ戦い…。
とりあえず前回の更新から進んだ部分を羅列しよう…。

  • 自己紹介ページを設置
  • Ajaxチャットの暫定版を設置
  • 他のページにいてもチャット入室・退室者のアナウンスが流れる仕組みに変更

次に開発中に発生した比較的クリティカルなバグと、実施した対策を羅列。長くなるなー。

IEのGETメソッドで送信されるパラメタに含まれる日本語はUTF-16?にエンコードされる

ついでに言うとデフォルトではエスケープ(URLエンコード)もされない。
ちなみにOperaだとUTF-8にエンコードされる。
現象は結構早くから確認した。とほほの方でも触れていたが、今まで気づかなかったのが不思議。
と言うのも、今まで作ってきたアプリケーションは日本語パラメタをすべてPOSTで受け取っていたのだ。
JSPではその辺を勝手にやってくれるので気づかなかったというハナシ。

対策としては、JavaScript側でescape関数を用いてエスケープし、Perl側でUnicodeモジュール・Encodeモジュールを用いてデコードした。
Perlの内部コードはUTF-8なのだが、ログファイルへの書き込みはシフトJISを用いているので、流れとしては

文字列(シフトJIS)→UTF-16エンコード→URLエンコード(=escape)→(送信)
(受信)→URLデコード(UTF-16)→UTF-16デコード→UTF-8エンコード→UTF-8デコード→シフトJISエンコード→文字列(シフトJIS)

というややこしい形に。 UTF-8をカマせるのは、単純にUnicodeモジュールがUTF-16→Shift_JISに対応しておらず、EncodeモジュールではUTF-16を解読できないためである。
モジュール間の引渡しにUTF-8を使用する形になる。
…と思ったら、どうもEncodeモジュールでUTF-16が読める模様。
意外と参考資料が少ない上に、perldocもあまり頼りにならないのが難点だな…。

入室者IDに+が含まれた場合、半角スペースに置換され、IDの照合ができない

これはまぁ、そこまでたいした問題ではないけれども…上の「デフォルトではエスケープされない」エラーと同じ括り。
JavaScript側でencodeURIComponent関数を用いることで解消。
しかしこれが少々問題で、正直まだ自分の中でも混乱している(上の問題もそうだが)。
まだ調査段階なので詳しい事は言えないが、escape関数・uncodeURIComponent関数のブラウザごとの振る舞いの違いにかなり問題があると考えて良い…。
ちなみにIEではescape関数ではUTF-16、encodeURIComponent関数ではUTF-8にそれぞれエンコードしてからエスケープされるみたいだ。

もうやめて!とっくにCPUの使用率は100%よ!〜CGIの限界〜

題で分かると思うが、Perl/CGIの限界を目の当たりにした。
今回のAjaxチャットは1秒ごとにサーバからXMLでチャットログをダウンロードするのだが、
1秒ごとに問い合わせを行うと、その都度Perl起動→コンパイル→実行→プロセス破棄という一連の流れが行われる。
ちなみに5人ぐらい(平均到着間隔:200ms)でCPUに限界が来た。当たり前だコンチクショー。

Perl/CGIが遅いと言われる理由はここにあって、「問い合わせのたびに 起動→コンパイル→実行→破棄 が行われる」という流れが、CPU資源を圧迫するのである。
Perlはいい子。Perl悪くない。悪いのCGI。
大量のトラフィックを捌かなければならないWebシステムにおいて、これはあまりにも無駄が多すぎる。

解決策を広大なネットで探してみたところ、いくつか案が出てきた。

  • mod_perlを使用する
  • FastCGIを使用する

他にも方法はあるけど大まかな仕組みは同じだったりするので、アプローチの違うこの2つを検証していこう。

mod_perlを使用する

mod_perlはつまるところ、「CGIではなくApacheのモジュールとしてPerlインタプリタを実装してしまおう」というわけである。
初回起動時にコンパイルしたPerlのバイトコードはソースに変更が加えられたり破棄されない限り、メモリ上で再利用される。
PerlインタプリタがApacheのモジュールとして起動しているため、毎回Perlインタプリタを呼び出しに行く作業が省略できる。

が、ランタイムコンパイルがかなり高速になっている現在のPerlにとって、コンパイルは大したオーバヘッドにならない。
あくまでインタプリタ起動にかかる時間を省略できる事がmod_perlの真骨頂である。
ちなみにPerlスクリプトに関してはちゃんと変数を初期化してstrictに書いていればコードの変更無しに動作させられる事が多い。

デメリットとしては、「Apacheのモジュールとして動作するので、多くのスクリプトを起動させるとメモリ空間を圧迫する」が大きい。
確かに高速化はできるが、「意図的に削除されない限りメモリ上に常駐する」のでは、Apacheの使用メモリ空間がえらい事になる。
チャット以外の日記用プログラムなどが高速化されても、呼び出し回数は多くないのにメモリはそんな事はお構いなしに消費されるのだ。

加えて、「PerlのエラーがApacheに直結する」のも大きい。
Perlスクリプトにバグがあった場合、その影響でApacheがダウンする可能性を孕む事になる。
mod_perlを実装していないサーバで入念なテストをしてから積み替えるのは、オレの様なスパゲッティ職人には苦痛だ。

FastCGIを使用する

FastCGIはPerlに限らず、CGIを使用する多くの言語で使える仕組みである。
こちらはFastCGIプロセス(Perlの場合はperl)をメモリ上に常駐させ、Apacheから引き渡されたリクエストに対して応答する。
メモリ上にはいくつか(設定による)のFastCGIプロセスが待機しており、リクエストに応じてアクティベートされたプロセスがユーザと通信を行い、通信が終了すると待機状態に戻る。
mod_perlとの違いは、「CGIと同様にApacheとは別プロセスで動作する」ところだ。
スクリプトのエラーが影響を及ぼすのはFastCGIプロセスだけなので、安全である。

デメリットとしては「スクリプトをFastCGIプロセスに対応させる必要がある」ことだろう。
Perlの場合はCGI::Fastパッケージを使用し、ソースコードにも変更が必要になる。

で、色々考えた末、FastCGIを導入する形にした。理由は以下の通り

  • CGIとFastCGIは共存できるため、高速化が必要なスクリプトだけFastCGIプロセスとして動作させられる
  • スクリプトに変更は必要だが、↑の理由で、高速化が必要なスクリプトだけ変更すればよい
  • CGIと同様にスクリプトエラーの影響がApacheに及ばないので、多い日も安心(バグ的な意味で)
  • mod_perlはApacheに依存しすぎてて、なんかカコワルイ。FastCGIインターフェースは標準化されているので、再利用性もそこそこ高い
  • スパゲッティが大好きです

そんなわけで、リクエストが頻発するAjaxチャット用APIであるget_entry.cgiとget_content.cgiをFastCGI用に変更し、
get_entry.fcgi・get_content.fcgiとした。

しかしWebAPIとAjaxでは相性が悪い気がするが…別サーバのCGIを呼び出せる仕組みがあるのだろうか。

問題点と対策の洗い出しはこんなところ。
他にも挙げればキリが無い小さな問題(主にJavaScript関連)があるが、ここでは割愛する。
相変わらずJSの基礎ができてないなぁ…と凹む事が多いorz
まだしばらくチャットの改良に明け暮れると思うが、趣味紹介ページもそろそろ作っていかなければならない。
気合と根性でやり切るぞー!(´・ω・`)ノ

記事トップに戻る