このブログの更新は Twitterアカウント @m_hiyama で通知されます。
Follow @m_hiyama

メールでのご連絡は hiyama{at}chimaira{dot}org まで。

はじめてのメールはスパムと判定されることがあります。最初は、信頼されているドメインから差し障りのない文面を送っていただけると、スパムと判定されにくいと思います。

参照用 記事

Erlang実験室:httpdをもう少し探る -- sleepプラグイン

「ErlangのWebサーバー・モジュールを試してみたよ」で、Erlang/OTP配布に付属のhttpdモジュールを紹介しました。今日は、httpdプラグインモジュールを作る練習をしました。「なんでhttpdなのか?」というと、僕の動機としては、Webサーバーをいじりたいつうより、Erlang分散プロトコル(の一部)をHTTPに乗せたいのです。

Erlangに手を付けたら、やっぱ分散プログラミングしないともったいないっすよ。非分散/非並列でErlangしてもあんまり楽しくないような … あの構文が好きなら楽しいかもしれないけど。あの構文が気に入らなくても、それを学ぶことが分散プログラミングの楽園(少しオオゲサか?)に入る入場料と考えれば高くはないでしょう。

そんな事情で、とりあえず簡単なhttpdプラグインモジュールを作ったわけですが、これは、URIに埋め込まれたパラメータ(秒数)だけ眠る(sleep)というシロモノです。なんかバカみたいですが、これでもけっこう実験に使えるのですよ; リクエストの長時間処理や長時間パーキング(接続をサーバー側で保持)を模倣できます。長時間処理/パーキングは、最近話題のCOMETとか非同期リクエスト処理(Asynchronous Request Processing)に関わります。眠るプロセス達により、同時接続数やHTTPパイプライン段数を確認することもできます。


%% File: mod_sleep.erl

-module(mod_sleep).
-export([do/1]).
-include_lib("inets/src/httpd.hrl").

-define(SLEEP_MAX_SEC, 5*60). % 5分

%% httpdプラグインモジュールとしてのコールバック・エントリーポイント。
do(ModData = #mod{data = OldData}) ->
% io:fwrite("mod_sleep called. URI:~s PID:~w\n",
% [ModData#mod.absolute_uri, self()]),
case {httpd_util:key1search(OldData, status),
httpd_util:key1search(OldData, response)} of
{undefined, undefined} ->
try_to_sleep(ModData);
_Other ->
{proceed, OldData} % 何もしないで次に送る
end.

%% sleepを実行するかどうかを判定して、必要なら実行する。
try_to_sleep(_ModData = #mod{data = OldData, request_uri = RequestUri}) ->
case check_and_get_param(RequestUri) of
no_match ->
{proceed, OldData};
NSec ->
do_sleep(NSec, OldData)
end.

%% リクエスURIを調べて、可能ならパラメータ値(整数)を取り出す。
check_and_get_param(RequestUri) ->
case RequestUri of
("/sleep/" ++ SecStr) ->
case (catch list_to_integer(SecStr)) of
{'EXIT', _} -> 0; % 構文エラーは0にしちゃう、面倒だから^^;
SecNum -> lists:min([SecNum, ?SLEEP_MAX_SEC])
end;
_Other ->
no_match % 失敗
end.

%% 指定秒数だけ眠る。
do_sleep(NSec, OldData) ->
console_write("mod_sleep: ~w sleeps ~w seconds.\n", [self(), NSec]),
timer:sleep(NSec * 1000),
console_write("mod_sleep: ~w wake up!\n", [self()]),
% 起きたぞ、レスポンスの組み立て
[PidText] = io_lib:format("~w", [self()]),
ContentText = PidText ++ ": " ++
integer_to_list(NSec) ++ " seconds slept.\r\n",
ResponseData =
{response,
[{code, 200},
{content_length, integer_to_list(length(ContentText))},
{content_type, "text/plain"}
],
ContentText
},
{proceed, [{response, ResponseData} | OldData]}.

%%%% 補助的関数

console_write(Format, ArgList) ->
io:fwrite(Format, ArgList).

使い方は、「ErlangのWebサーバー・モジュールを試してみたよ」で述べたとおりです。設定ファイルのModules行を次のようにします。


Modules mod_alias mod_auth mod_actions mod_sleep mod_cgi mod_responsecontrol mod_trace mod_range mod_head mod_include mod_dir mod_get mod_log mod_disk_log
mod_sleepをどこに挿入するのが最適か、よくわからなかったのですが、mod_cgiとかの近くであろうということでココラヘン。まー、動いてはいます。

ブラウザで、http://localhost:8888/sleep/3 にアクセスすると、Erlangシェルには次の表示が出ます。


mod_sleep: <0.1180.0> sleeps 3 seconds.
mod_sleep: <0.1180.0> wake up!
ここで、0.1180.0はこのリクエストを処理したプロセスのIDです。ブラウザ画面には3秒後に <0.1180.0>: 3 seconds slept. と表示されます。