まだ重たいCMSをお使いですか?
毎秒1000リクエスト を捌く超高速CMS「adiary

2006/07/24(月)β5リリース情報

β5は不具合修正がメインです。今回も adiary.conf.cgi は、変更なしで引き継げます。

  • "?"を含むURLにトラックバックが送れない不具合を修正しました。
  • WordPressにトラックバックを送れない問題に対処しました*1。それに伴い、トラックバック - adiary拡張仕様(日本向け)を改訂しました。
  • Windows環境で動作させた場合、ログをインポートできない=マルチパートフォームの処理に失敗するバグを修正しました。*2
  • スタートアップルーチンを各種見直しました。またFastCGIできちんと動作するよう修正を加えました。
  • README.txt を少し修正しました。
  • 【標準テーマ】Firefoxで横スクロールバーがでてしまうので、hatena-body の width を 98% に設定しました。
  • かえで氏作のテーマ(lfdiary test1, せっちゃん RC2)を収録しました。
  • その他修正

余談。だからどうしたという話ですが adiary は aDiary*3 とは全く関係ありません(^^;;

*1 : フォームに author というキーがあると誤動作する不具合。adiary拡張仕様と衝突してました。オリジナルトラックバック仕様書の規定外なのでバグと断定するのは微妙ですが、限りなく WordPress の不具合な気がする

*2 : ActivePerl + Apache/AnHttpd で確認。追記、AnHttpdではパス自動解析部がうまく動かないようです。次β版を待つか、conf ファイルの16行目付近にある Basepath を適切に設定してください。

*3 : PHPによる日記システム。2年ぐらい前(?)に開発停止らしい。

2006/07/22(土)テンプレート

Serene Bachをいじってみてる

「やっぱり普及するには、見た目(デザイン)は大切ですよ」

と言われたこともありまして、Serene Bachをいじってます。もちろんインポート/エクスポートで対応しているぐらいだから一度インストールしてるんですが。Amazonのオススメがスクリプト上から選べるのは楽でいいなぁ……とか思いながら、いざのテンプレート機能。ブラウザからアップロードできるのは楽でいいです。

Serene Bach/JUGEM系のテンプレートは、(ある意味でかたい感じのする)はてなのテーマに比べて愉快で楽しそうな感じのテンプレートが結構ありまして*1、使えるようにできたらいいなあと検討しています。

adiaryでテンプレートに相当するスケルトンは本当に何でもできるので*2、十二分にカバー可能なのですが、その一方で「なんでも出来すぎて普通に人にはいじりにくい」から「スケルトン(テンプレート)を生成するためのテンプレート機能が必要ではないか?」なんてお話を頂いたりしてました*3。たしかにそうかもしれない。

規格乱立はリソースの無駄遣いに他ならないので、その場合はやっぱりSerene Bach/JUGEM系互換を狙うのかなあとか考えております*4。すぐに対応というわけにはいきませんけど、検討は重ねておきます。意見とかありましたらどうぞ↓

*1 : ただ、GPLライセンス文化がないので配布場所が一箇所に纏まってなくてそういう意味では不便なのですが

*2 : 極端な話、第3者がSpyware的なテンプレートを配布することも可能なわけです……

*3 : adiaryはこうやってユーザーの要望に対応することで進化してきたので感謝、感謝。

*4 : 内部的にはテンプレートコンバータの作成と内蔵に他ならないのですが

2006/07/21(金)β4リリース情報

  • JavaScriptによるコメント/TrackbackのSPAM対策を行いました。
  • 1日1記事用の日記モードを搭載しました。
  • 書き込み時刻を表示する機能を付けました(日記帳の詳細設定 → 表示設定)。
  • 記事IDの代わりに書き込み時刻(UTC)を指定する方式を付けました(日記帳引っ越し対策)。
  • カテゴリ編集へのリンクが消えていたので修正しました。
  • JUGEM形式でエクスポートできない不具合を修正しました。
  • メニューの「自分の日記へ」のリンク先がおかしかったので修正しました。
  • 【標準パーサ】[hatena:help:リンクを簡単に記述する(http記法、mailto記法):http記法]のimage指定に対応しました。実装にはSimpleAPIを使用しています(Thanks to SimpleAPI)。
  • 【標準パーサ】ユーザー定義タグのオプション部に正規表現を書けるようにしました詳細
  • 【標準テーマ】h5 タグに属性「margin-bottom: 1em;」を追加しました。
  • スタイルシートの解釈がおかしいIE7β3の不具合に、部分的に対応しました。
  • その他細かい修正

SPAM対策ですが、JavaScriptということで落ち着きました。一応チェックはしていますが、不具合などありましたら遠慮無くご連絡ください。これを突破するspammer(ロボット)が現れたらまた何か考えましょう。

追記。今回から対応したFastCGIですが、念入りに確認してみるとイマイチ動作不安があるので、β4でのFastCGIの導入はお勧めしましせん。さらに追記。問題なく安定して動作するようになりました。β5以降をお待ちください

SPAM対策の方針メモ

要するに人間に認識しやすく、ロボットに認識しにくい方式を考えればよいのですが、JavaScriptや質問文その他の方策をいくら取ったところで、adiary向けの専用対策を取られたら太刀打ちは無理(イタチごっこ)になります。ですから逆に開き直って、とりあえずは JavaScript+JavaScriptがない環境では簡単なキーワード入力を促すという方式にしました。

あまりテストしていないので誤動作する or 特定ブラウザでうまくコメントを書き込めない*1ようならご連絡ください。

*1 : セキュリティエラーとなる

せっかくなので

http記法のimageで遊んでみる。


おまけにTeX記法を使ってみる。

57+9+30=96

2006/07/19(水)スケルトンのカスタマイズ

この記事はVer2以前のものです。Ver3は無関係です。


skeltonを直接カスタマイズしたいという要望があるようなので、カスタマイズの仕方を説明します。スケルトンカスタマイズにはプログラム的知識が多少必要になります。

※2007/01/24 大幅に加筆しました。バージョンアップ時の手間を考えた場合、_main, _sidebarスケルトンの変更はあまりお勧めしません。「詳細デザイン」+トラストモードで済む場合はそちらを検討してください。

スケルトンディレクトリ diary.skel/

adiaryの機能はほぼすべてこの中に納められています。adiaryのスクリプトに対する引数

adiary.cgi?diary_edit

であれば、diary.skel/diary_edit.html が呼ばれます。カスタマイズしたい機能をみつけたい場合は、その画面のURLに着目すればよいことになります。なおセキュリティ上の制約で、"_" ではじまるスケルトンおよびサブディレクトリにあるスケルトンは引数では直接呼べません。

なお、スケルトンをカスタマイズする際は、diary.skel のを書き換えるのではなく、diary.user.skel にコピーしてから書き換えるようにしてください。こうすれば diary.skel より優先して読み込まれます。

ですが同時に、バージョンアップをしても常にユーザースケルトンが優先されてしまうので、オリジナルが大幅に書き変わっている場合など多少注意が必要です。後述する特殊スケルトンのうち _main, _main_onelog, _sidebarスケルトンは比較的よく改変されます*1。この3つについては、変更点をバージョンアップアナウンス時にアナウンスしていますのでご注意ください。

あまり使いませんが

diary.user.skel などにおいたユーザースケルトンでは

<$continue()>

という関数(コマンド)を使うことで、本来の diary.skel 内の該当スケルトンに処理を引き継ぐことができます。theme/hatena/_skelton/_frame.htmlなどで使用しています。

*1 : _multiuser_top はまず改変されません

特殊な役割をするスケルトン

まず重要なのが外側のフレームを生成するスケルトンです。通常、adiaryの画面は、該当スケルトンを処理し、その結果を埋め込む形でフレームスケルトン(_frame.html)が呼ばれます。サイドバーは、フレームスケルトン内から呼ばれています。

その他特殊なスケルトンは以下の4つです。

_main日記本文を生成するスケルントンです。
_main_onelog日記本文生成ですが、記事ID指定時の表示に使われます。
_sidebarサイドバーを生成するスケルトンです。
_multiuser_topマルチユーザーモード時のトップ画面です。

このblogレンタルサービスでは、_multiuser_top.html をカスタマイズして diary.user.skel/ に置いてあります。

デザインテンプレート

スケルトンをテーマディレクトリに置くことで「表示画面テンプレート」を作ることができます。例えば、携帯電話用テンプレート(HTMLの元)はtheme/_phone/_skelton/ に置かれています。

デザインテンプレートを作成する際は、theme/ 以下に任意のディレクトリを作成してください。この場合、テンプレート名と同名のテーマ(CSS)が必要になります。中身が空っぽでもいいので必ず置いてください。

theme/my_design/ ならば
theme/my_design/_skelton/ に任意のスケルトンを置き
theme/my_design/my_design/my_design.css というテーマファイルが必要

(選択中の)テーマディレクトリのスケルトンはユーザースケルトンよりも優先されます(しかしシステムモードではテンプレート(テーマディレクトリスケルトン)は無視されます)。

スケルトンの文法

adiary は Satsuki system という独自のスケルトンシステムにより作成されています。膨大な機能があるシステムですが、文法を簡単に説明しておきます。プログラム経験のある人ならさほど悩まないとは思いますが。

行コメント

<@> コメント

<@> で始まる行はコメントです。現在の仕様では、行の途中に <@> を書いてもコメントとして扱われますが、今後の仕様変更の可能性がある部分ですので、必ず行頭に書いてください。

実行構文

<$xxxx>
<@xxxx>

これらは「式」として評価されます。コマンドと言ったりもします。<$~>は実行されますが何も出力されません。<@~>は実行後の結果が出力されます。<@~> は次のような変数置換の形で最もよく使われます。

<li><a href="<@v.this_archive_dir><@v.rss_file>">RSS</a>
<li><a href="<@v.myself>?diary_list">過去日記の一覧</a>
<li><a href="<@Myself2><@v.path_info>?theme/_print/_print">印刷用の表示</a>

実行構文のコメントアウト

<#$xxxx>
<#@xxxx>

このように、コマンド内の先頭に # をつけることで、該当コマンドをコメントアウトできます。コメントアウトしても構文内の ( ) 対応や " "対応はチェックされます。

最適化構文

<@20> 最適化on

コンパイル最適化を指示します。基本的に書き換えないでください。

変数(名前空間)

Satsuki-systemでは、変数の名前空間をピリオドで区切ります。変数名は大文字小文字で区別されます

<@Myself>
<@v.rss_file>

前者は、ルートの中の Myself という変数を、後者は v の中の rss_file というものを示します。

Satsuki-systemでは、小文字で始まる変数名(名前空間)はユーザーに解放されていますが、設計時からの慣習で利用時のメインシステム(この場合adiary)を変数 v の中に入れることになっています。adiary のスケルトンではこのほか、変数 s に各ユーザーの日記帳情報(日記帳の設定情報など)を入れる約束になっています。

このことから、vs以外の小文字で始まる変数はカスタマイズ時に自由に使うことができます*2

begin/else/end

これについては、せりかさんの解析記事を参考にしてください。

Satsuki-systemの仕組み

この項目は Perl のオブジェクト指向に理解のある方以外は読まないでください(混乱するだけです)。

Perl では、無名のハッシュリファレンスにライブラリ名(ライブラリ名前空間)を結びつけることでオブジェクト指向を実現しています。Satsuki-system の名前空間というのは、adiary.cgi*3 で生成される Satsuki::Base のオブジェクト $ROBJ の中身そのものです。そしてよく見かける loadpm の実体は new そのものです(ライブラリをロードしてオブジェクトを new して返す)。

Satsuki-system名前空間と実体の対応関係は、次のようになっています。

Satsuki-system実体
<@Myself>$ROBJ->{Myself}
<@v.rss_file>$ROBJ->{v}->{rss_file}
<@call("sub/sub")>$ROBJ->call("sub/sub");
<$v=loadpm("XXX")>$ROBJ->{v} = $ROBJ->loadpm("XXX");

PerlのTemplateライブラリなどではスケルトンに対しハッシュをバインドできるようですが、Satsuki-system では、システム全体をスケルトンシステムの中に配置しています。Satsuki-system の驚異的な柔軟性と開発効率の要はここにあります。*4

Templateライブラリシステム ->テンプレート
Satsuki-systemスケルトン ->システム

実際、adiary.conf.cgi というスタートアップスケルトンを用意しなければ、Satsuki-system はまったく動きませんし、逆にスタートアップスケルトンだけでもある程度のCGIスクリプトを記述するも可能です。

ちなみに adiary.conf.cgi に <$Compile_log_dir="data/log/"> を定義すると、スケルトンが perl スクリプトへコンパイルされる様子をログとして見ることができます(遅くなるので通常はオフにしておきましょう)。

*2 : 一部利用している変数名もありますが、かぶることはないでしょう

*3 : Satsuki-system 共通スタートアップルーチン

*4 : Satsuki-system はいずれは単独公開する予定ですが。

2006/07/19(水)Perl CGIのキャッシュ環境

FastCGI対応は書き間違えだったのですが、どうせならということで対応してみました(対応版の配布はβ4以降になります)。

Perl CGIのキャッシュ動作比較

気づいたらあれこれ対応しすぎた感じがありますが、せっかくなのでどれが一番速いのか試してみしまた(笑) いずれの設定も全くチューニングも何もしていませんので、参考程度にお願いします

CPU  : Pentium3 800MHz (133*6/Coppermine)
OS   : FreeBSD 6.1-PRERELEASE
Web  : Apache 2.2 (prefork)
ETC  : DBキャッシュON
TEST : ab -n100 -c4 http://127.0.0.1/xxx/adiary.cgi/user/

テストしたページには適当な日記が数件表示されています。結果は数回実行して頻出値に近い値を取りました。

動作モードReq/secms/Req
cgi2.10477.270
speedycgi15.1965.825
mod_speedycgi26.4137.863
mod_fastcgi21.2846.990
mod_fcgid21.2147.157
mod_perl2*126.6937.473
text-file196.75.084

mod_fastcgiとmod_fcgidは差がなくて、mod_perl/mod_speedycgiが一歩前に出てるという感じですね。worker動作(スレッドモデル)となると、対応しているのは mod_perl2 vs mod_fcgid だけ。mod_perl2 はいかんせん導入が面倒くさいので、手軽さでは mod_fcgid の方がよいのかもしれません。

本格的にパフォーマンスを求めたり、高負荷時のメモリ消費量の少なさを考えると mod_perl2 on worker MPM に優る選択肢はないのですが個人では必要ないでしょう。*2

ただ、どれも Apache にモジュールを組み込まないとならないので、お手軽に高速化したい場合はSpeedyCGIソースコード)をオススメします。パフォーマンスも(個人で使うには)十分ですし、Apacheからは完全にcgiとして見えるので(プロセスが完全に分離するので)、精神的にもよいです。

誰か同じことをMTの管理画面で試さないかなぁ(笑)

*1 : Apache::Reloadなどのハンドラを外してやれば1ms程度改善しました

*2 : メモリ消費量を計測したわけではありませんが、mod_perl2/worker ではPerl自体もスレッド動作になるので、1リクエスト=1プロセスの mod_fcgid 系では勝負にならないと思います

各方式の特徴

せっかくなので、簡単に仕組みと特徴を説明。

Perl/cgi

通常のcgi動作。Apacheが、該当のcgiプログラムを fork し実行します。Perl/cgiでは、cgiファイル(使用ライブラリファイル群)のコンパイル時間がとても長く、度々問題となります。*3以下の方式は、どれもこのコンパイル時間を減らすことで大きな高速化を果たしています。

SpeedyCGI(=PersistentPerl)/mod_speedycgi

Apacheからみたら通常のcgiプログラムですが、スクリプト1行目にPerlではなくSpeedyCGIを起動するための

#!/usr/bin/speedy

を記述することでSpeedyCGIが動作します*4。SpeedyCGIは内部的にPerlを呼び出し、実行終了後もそのプロセスを常駐させ(バックエンドと言う)、続いてリクエストが来たときに空いたバックエンドがあればそれに処理を行わせます。

スクリプト側から見た場合、Apacheプロセスと分離されることを除けば*5、全体的な動作はほとんどmod_perlと同じです。

歴史が浅いのか何なのかいまいちマイナーだけど、導入も容易で、mod_perlでは動作しないスクリプトも動作し、Apacheとは別プロセスになるので使うのも気楽です。SpeedyCGIをインストールしてあるレンタルサーバもみかけます。

mod_speedycgiはSpeedyCGIの管理プログラム起動コストを押さえるためApacheにモジュールとして組み込んだものです。それによりかなり高速化されますが、worker MPMには非対応なので注意が必要です。

FastCGI/mod_fcgid

SpeedyCGI的な考え方をより推し進めて、Apache内部にバックエンドプロセスを管理する機能をモジュールとしてインストールしたものです。SpeedyCGIでは、いくらバックエンドがあるとはいえ結局は別プロセスである SpeedyCGI(/usr/bin/speedyそのもの)を fork して実行する必要がありましたが、FastCGIではバックエンドを直接操作するため fork のオーバーヘッドがありません。

mod_fastcgi(FastCGI)とmod_fcgidの差ですが、前者をスレッド動作のApacheに組み込むのはあまりよろしくないとのことです。また巷の噂によると後者の方が速いとかなんとか。なお、FastCGIをスレッド動作のApacheに組み込んだとしても、CGIプログラム(Perlなど)自体は1プロセス=1クライアントとなります。

cgiスクリプトはFastCGI向けに改造する必要があります。

use FCGI;
my $count = 0;
my $request = FCGI::Request();
while($request->Accept() >= 0) {
	print("Content-type: text/html\r\n\r\n", ++$count);
}

mod_perl

バックエンドどころか、「Perl自体をApacheに抱え込んでしまえばいい」という荒技的な解決策を提示するのが、mod_perl。荒技なだけに、もっともオーバーヘッドが少ない*6方式です。mod_perlの特徴として、Apacheの内部動作をスクリプト側から事細かに制御できますが、移植性(配布性)を考えたソフトではあまり使うことはありません*7

mod_perlには、Apache 1.x系向けのいわゆるmod_perl(以下mod_perl1と表記)と、Apache 2.0/2.2系向けのmod_perl2があり全く互換性がありません*8。この互換性が度々問題となって、mod_perl1向け書かれたcgiをmod_perl2で動かそうとすると誤動作して厄介な問題を引き起こします。その代表選手とも言えるものがMovable Type*9

スクリプト側から見た場合、mod_perl1やスレッド動作でないApache2ならば、およそSpeedyCGIと同じような感じです。ただ、Apache2が真価を発揮する worker(スレッド)動作でのmod_perl2は、Perlスクリプト自体もスレッド動作することで高い効率が得られる一方、カレントディレクトリを指定出来ない(chdir()できない)こと*10、あらゆるスクリプトがマルチスレッドでライブラリ空間(メモリ空間)を共有すること*11が大きなネックになります。後からこの対応を行うことは困難であり、これがmod_perl2向けスクリプトがほとんど存在しない原因になっていると思われます。

余談

Apache2(Prefock) + mod_perl2 のときは、次のようにすると自動で chdir されます(いずれか1つ選択)。

PerlResponseHandler ModPerl::RegistryPrefork
PerlResponseHandler ModPerl::PerlRunPrefork

*3 : 不必要なライブラリを極力ロードしないように気を付けているadiaryですら、cgi実行時間の約9割がPerlのコンパイル時間です。

*4 : この仕組みから分かるとおり、SpeedyCGIは何もcgiだけではなく、通常コンソールから実行するPerlスクリプトに対しても高速化が可能

*5 : Apache2のworkerであっても、chdirやumaskなどプロセス環境を変更することができる

*6 : 原理上もっとも速く動作する

*7 : Apache自体の動作を変えてしまうような、特殊なことをする専用アプリ開発には向いている

*8 : Apache2では、スレッド動作による効率化を求めたため、Apacheと一体となる mod_perl はApache 2系向けに大幅な改変が施されている

*9 : パッチを充てたり、mod_perl2の設定をあれこれ変更したりと涙ぐましい努力がたくさん見られます

*10 : サーバ全体で1つディレクトリにあるmod_perl2スクリプトしか実行しないとすれば、なんとかなるかも知れませんが……。きわどい。

*11 : 特定関数内で一時的であってもグローバル変数にデバッグフラグを立てるモードを設定するなどということが出来ない。