というわけで、ErlangベースのMMOエンジンを構築中です。
ブログのエントリがまるっと止まってしまっていてお恥ずかしい限りですが。。。。
ちまちまとコードを書いているところですが、CI(継続的インテグレーション)ツールのJenkins(旧Hudson)と、Erlangのユニットテストツール「EUnit」を使って、進捗と現状の把握がしやすい環境を整えようとしているところです。
↓のエントリを参考にさせていただきました。
http://shin1o.blogspot.com/2008/02/hudson.html
全体的には、テストコードを用意して、CIのビルドコマンドにテスト実施を指示し、テストはsurefire形式のログを吐き出すよう記述する、という組み合わせになります。
開発本家ブログには、Integrating Eunit test into Hudson(Jenkins) CI process.として記録していたのですが、書き慣れない英語よりも日本語でふつーにメモしておかないとあとで自分が困りそうです。。。
■手順はこんな感じ。
1.テストコードを書きます。さらに、EUnitのテストコードをまとめて呼び出すエントリポイントを作ります。
run_tests_with_log() -> eunit:test([test], [{report,{eunit_surefire,[{dir,"."}]}}]).
2. Makefileに、上記テストを呼び出すコードを書きます。
test: $(objs)
erl -pa . -noshell -boot start_clean -s test run_tests_with_log -s init stop
(このエントリは、testターゲットが test:run_tests_with_log/0 を起動します)
3. Jenkins(Hudson)に、以下のプラグインをインストールします。
Cobertura Plugin
FindBugs Plug-in
4. Jenkins(Hudson)のJobに“make test”を追記。さらに、“Publish JUnit test result report” をオンに、XML Pathを"src/*.xml"などに設定し、レポートXMLを参照するよう指定します。
5. 以上の設定で、自動ビルドのときにテストまで行われるようになります。
2011年03月30日
2010年05月18日
セッション維持にIPアドレスを判断基準にしない。特にIPv6。
IPv6オペレーターズフォーラムの過去資料を見ていてびっくり。IPv6では端末が追跡可能にならないよう、IPアドレスを動的にどんどん変えていく機能があるようです(関連 RFC3041)。しかもWindowsXPとVista(ということはおそらく7も)は実装しているらしい。
つまり、IPアドレスが変わったからといってセッションを切断してはいけない、ということですね。
・・・セッションが乗っ取られにくいように、IPアドレスが変わったら黙って切断しようと思っていたのですが、それ以外の方法をとらないといけないようです。
危なかった。。。そのまま実装してたら「なぜか切れる人」が出まくるところだった。。。
つまり、IPアドレスが変わったからといってセッションを切断してはいけない、ということですね。
・・・セッションが乗っ取られにくいように、IPアドレスが変わったら黙って切断しようと思っていたのですが、それ以外の方法をとらないといけないようです。
危なかった。。。そのまま実装してたら「なぜか切れる人」が出まくるところだった。。。
2010年01月16日
spawnは慣れるまで「spawn_link」で書かないといけませんね
Erlangで、別プロセスへのメッセージ送信の練習をするのだ−、ということで、メッセージ受信部をspawnしておいて、そのあとメッセージを連投する、というコードを書いてみたのですが、なぜかreceiverのio:formatが受信したメッセージを表示してくれないという現象で一晩ハマりました。
バグとしては単純で、io:formatの追加引数のところをリスト化し忘れていたために表示されなかったのですが、まったくエラーとかが出なかったのでハマッたわけです。
結局、spawnをspawn_linkに置き換えて、生成したプロセスがエラーを発生させて止まったときに、親のプロセスも止まるようにしたら、エラーメッセージが出て原因箇所がつきとめられました。
親に影響が波及しては困るプロセス生成もあるのでしょうけど、そんなのは先の話。ひとまず初心者のうちはspawn_linkと書くようにしますー
--[期待した出力]--------
1> c(procs).
{ok,procs}
2> procs:testit().
Message from 5
Message from 4
Message from 3
Message from 2
Message from 1
ok
3>
-------------------------
--[実際の出力]-----------
1> c(procs).
{ok,procs}
2> procs:testit().
ok
3>
-------------------------
--[spawn_linkに置き換えたときの出力]-----------
10> procs:testit().
Message send 5
Message send 4
Message send 3
** exception exit: badarg
in function io:format/3
called as io:format(<0.25.0>,"Message from ~p~n",5)
in call from procs:receiver/0
11>
-----------------------------------------------
--[動かなかったコード]-----------------------
-module(procs).
-compile(export_all).
testit() ->
Pid = prep(),
timer:sleep(3000),
kicktest(Pid, 5).
prep()->
spawn(?MODULE, receiver,[]).
receiver() ->
receive
{num, A} ->
io:format("Message from ~p~n", A),
receiver();
{stop} ->
io:format("bye ~n");
_Other ->
io:format("Unknown message~n"),
receiver()
end.
kick(Pid, A) ->
Pid ! {num, A}.
kicktest(_ReceiverPid, 0)
-> ok;
kicktest(ReceiverPid, Count) when Count > 0 ->
kick(ReceiverPid, Count),
kicktest(ReceiverPid, Count -1).
-----------------------------------
--[ちゃんと動くコード]-----------------------
-module(procs).
-compile(export_all).
testit() ->
Pid = prep(),
timer:sleep(3000),
kicktest(Pid, 5).
prep()->
spawn(?MODULE, receiver,[]).
receiver() ->
receive
{num, A} ->
% ここ、Aをリスト化し忘れてた↓
io:format("Message from ~p~n", [A]),
receiver();
{stop} ->
io:format("bye ~n");
_Other ->
io:format("Unknown message~n"),
receiver()
end.
kick(Pid, A) ->
Pid ! {num, A}.
kicktest(_ReceiverPid, 0)
-> ok;
kicktest(ReceiverPid, Count) when Count > 0 ->
kick(ReceiverPid, Count),
kicktest(ReceiverPid, Count -1).
-----------------------------------
バグとしては単純で、io:formatの追加引数のところをリスト化し忘れていたために表示されなかったのですが、まったくエラーとかが出なかったのでハマッたわけです。
結局、spawnをspawn_linkに置き換えて、生成したプロセスがエラーを発生させて止まったときに、親のプロセスも止まるようにしたら、エラーメッセージが出て原因箇所がつきとめられました。
親に影響が波及しては困るプロセス生成もあるのでしょうけど、そんなのは先の話。ひとまず初心者のうちはspawn_linkと書くようにしますー
--[期待した出力]--------
1> c(procs).
{ok,procs}
2> procs:testit().
Message from 5
Message from 4
Message from 3
Message from 2
Message from 1
ok
3>
-------------------------
--[実際の出力]-----------
1> c(procs).
{ok,procs}
2> procs:testit().
ok
3>
-------------------------
--[spawn_linkに置き換えたときの出力]-----------
10> procs:testit().
Message send 5
Message send 4
Message send 3
** exception exit: badarg
in function io:format/3
called as io:format(<0.25.0>,"Message from ~p~n",5)
in call from procs:receiver/0
11>
-----------------------------------------------
--[動かなかったコード]-----------------------
-module(procs).
-compile(export_all).
testit() ->
Pid = prep(),
timer:sleep(3000),
kicktest(Pid, 5).
prep()->
spawn(?MODULE, receiver,[]).
receiver() ->
receive
{num, A} ->
io:format("Message from ~p~n", A),
receiver();
{stop} ->
io:format("bye ~n");
_Other ->
io:format("Unknown message~n"),
receiver()
end.
kick(Pid, A) ->
Pid ! {num, A}.
kicktest(_ReceiverPid, 0)
-> ok;
kicktest(ReceiverPid, Count) when Count > 0 ->
kick(ReceiverPid, Count),
kicktest(ReceiverPid, Count -1).
-----------------------------------
--[ちゃんと動くコード]-----------------------
-module(procs).
-compile(export_all).
testit() ->
Pid = prep(),
timer:sleep(3000),
kicktest(Pid, 5).
prep()->
spawn(?MODULE, receiver,[]).
receiver() ->
receive
{num, A} ->
% ここ、Aをリスト化し忘れてた↓
io:format("Message from ~p~n", [A]),
receiver();
{stop} ->
io:format("bye ~n");
_Other ->
io:format("Unknown message~n"),
receiver()
end.
kick(Pid, A) ->
Pid ! {num, A}.
kicktest(_ReceiverPid, 0)
-> ok;
kicktest(ReceiverPid, Count) when Count > 0 ->
kick(ReceiverPid, Count),
kicktest(ReceiverPid, Count -1).
-----------------------------------
2010年01月07日
MacOS上でソース編集するときは改行コードに注意
MacOS XでErlangのソースを書いていてコメントまわりで軽くハマッたのでメモ。
MacOS Xではなぜかコメント(「%」以降、行末までコメント扱い)がうまく動かないという現象が起きていました。
原因は、Erlangの実行環境 erl は、CRのみの改行を改行と認識しないためでした。エディタ上では改行しているつもりでも、erlは長い1行と認識。そのため、コードの途中でコメントをつけると以後の行がすべてコメント扱いとなっていました。
テキストエディタに改行コードの変更機能があれば、それを使ってLFもしくはCRLFを改行コードに設定することで回避できます。
MacOS Xではなぜかコメント(「%」以降、行末までコメント扱い)がうまく動かないという現象が起きていました。
原因は、Erlangの実行環境 erl は、CRのみの改行を改行と認識しないためでした。エディタ上では改行しているつもりでも、erlは長い1行と認識。そのため、コードの途中でコメントをつけると以後の行がすべてコメント扱いとなっていました。
テキストエディタに改行コードの変更機能があれば、それを使ってLFもしくはCRLFを改行コードに設定することで回避できます。
2009年12月22日
Erlang Super Lite Chapter3に参加予定
あまりに手が止まってしまっているので、勉強会に参加して無理矢理にでも手を動かそうと決心。
ひとまず1月21日のErlang勉強会、Erlang Super Lite Chapter3に参加予定です。おもしろそう。
課題図書は、オライリーの原書、Erlang Programming
。さっそく購入して、宿題もぼちぼち解いてますー
ひとまず1月21日のErlang勉強会、Erlang Super Lite Chapter3に参加予定です。おもしろそう。
課題図書は、オライリーの原書、Erlang Programming
2009年10月21日
開発環境としてのMacOSが便利すぎる
新型Macが発表になりましたが、値段が下がってたり、iMacのスクリーンがさらに大きくなってたり(最上位はCore i5でクワッドですよ。。。)、ずいぶんとよさげです。
で、Erlangの開発環境としてWindowsでずっとやってたのですが、1ヶ月ほど前にMacBook Airを買ってからはMacばかりになってしまいました。仮想化ソフトのVirtualBox(無料)にWindowsも入れていますが、VirtualBox自体ほとんど立ち上げないです。
MacOSは中身がUnixなのでviとか標準で入っているし、追加ソフトのインストールもMacPortsを使えば非常に簡単。ErlangもYawsも、MacPortsからインストールして問題なく使えています。
ページやアプリを作って、Safari/Firefox/IEでテストする、というのが1台でできちゃうのは本当に便利。
EclipseでErlide(Erlangプラグイン)を使う場合は、Eclipseを3.4にしておく必要がありますが、3.4もまだ配布されているので特に問題はないかと(3.5だとエラーが出てErlangモードが利用できませんでした)。
オフィスソフトもファイルの読み書きだけならOpenOffice.orgで問題なし。
新しいOSを導入したにもかかわらず、ATOKとWindows以外はお金かからないというのはすごいなあ。昔なら何やかやで十万円近くかかるところだよね。
あ。ATOKは1 Yearライセンス(3360円/年)を選択。LeopardからSnow Loepardにアップグレードされたときに以前のATOKが動かなくなったと聞いたので、パッケージで購入するメリットがいまいち感じられなかったので。年額3360円なので、月額300円の方をチョイスしてもあまり大差なかったかも。。。
で、Erlangの開発環境としてWindowsでずっとやってたのですが、1ヶ月ほど前にMacBook Airを買ってからはMacばかりになってしまいました。仮想化ソフトのVirtualBox(無料)にWindowsも入れていますが、VirtualBox自体ほとんど立ち上げないです。
MacOSは中身がUnixなのでviとか標準で入っているし、追加ソフトのインストールもMacPortsを使えば非常に簡単。ErlangもYawsも、MacPortsからインストールして問題なく使えています。
ページやアプリを作って、Safari/Firefox/IEでテストする、というのが1台でできちゃうのは本当に便利。
EclipseでErlide(Erlangプラグイン)を使う場合は、Eclipseを3.4にしておく必要がありますが、3.4もまだ配布されているので特に問題はないかと(3.5だとエラーが出てErlangモードが利用できませんでした)。
オフィスソフトもファイルの読み書きだけならOpenOffice.orgで問題なし。
新しいOSを導入したにもかかわらず、ATOKとWindows以外はお金かからないというのはすごいなあ。昔なら何やかやで十万円近くかかるところだよね。
あ。ATOKは1 Yearライセンス(3360円/年)を選択。LeopardからSnow Loepardにアップグレードされたときに以前のATOKが動かなくなったと聞いたので、パッケージで購入するメリットがいまいち感じられなかったので。年額3360円なので、月額300円の方をチョイスしてもあまり大差なかったかも。。。
2009年10月20日
Mnesiaでテーブル構造を変えたときは
Erlangの標準データベース管理システム「Mnesia」は-recordからテーブル構造をつくれたり、保存できるデータもErlangのデータ構造と親和性が高かったりと、非常に便利です。
が、「プログラミングErlang
」のMnesiaの説明はちょっとだけ不親切というか、サンプルコードを変更して自分用にいろいろしていくときに肝心な説明が抜けてる。
テーブル構造を変えたときにどう作り直すかが書かれてない。まあ、仕方ないのかもしれないけど、とりあえ初学者はハマると思う。少なくとも自分ははまりました。
対処としてシンプルなのは、ノードが保存している古い情報をクリアしてテーブル構造の構築からやりなおさせるという手順。
mnesia:stop(), mnesia:delete_schema([node()]).
です。
これであらためてdo_this_once()←サンプルにあるデーブル初期化コードから実行すれば、あたらしいテーブル構造が使えるようになります。
たぶんdelete_schemaではなくテーブル単位での消去もあると思うけど、とりあえず自分のメモもかねてこれを記録しておきます、はい。
が、「プログラミングErlang
テーブル構造を変えたときにどう作り直すかが書かれてない。まあ、仕方ないのかもしれないけど、とりあえ初学者はハマると思う。少なくとも自分ははまりました。
対処としてシンプルなのは、ノードが保存している古い情報をクリアしてテーブル構造の構築からやりなおさせるという手順。
mnesia:stop(), mnesia:delete_schema([node()]).
です。
これであらためてdo_this_once()←サンプルにあるデーブル初期化コードから実行すれば、あたらしいテーブル構造が使えるようになります。
たぶんdelete_schemaではなくテーブル単位での消去もあると思うけど、とりあえず自分のメモもかねてこれを記録しておきます、はい。
2009年10月17日
アンサー:ErlangとYawsを使ったRESTfulなサービス@InfoQ
RESTfulな形でオンラインゲームサーバーを構築しようと考えていて、検索したら出てきたのがこの記事。
ErlangとYawsを使ったRESTfulなサービス@InfoQ
これを読みながら「ほうほう、yawsにはout/1以外にもout/3なんていうつなぎ方があるのか。それなら勝ったも同然だな」と思ったのですが、記事にある関数宣言に従ったモジュールをyawsにくわせても、ちーっとも動かない。
で。しばらく考えて気がついた。
InfoQさんの記事は「out/3を自分で作るとRESTfulが作りやすくなるぜ」という意味だったのです。。。。
というわけで、とりあえずout/3を書いてみました。ちょろっと書くだけでmod_rewriteでごりごりやるようなRESTfulな処理が動くのはすごい便利。Yawsはぜひ使いこなしたいですね。
このときのyaws.confはこんな感じ
ErlangとYawsを使ったRESTfulなサービス@InfoQ
これを読みながら「ほうほう、yawsにはout/1以外にもout/3なんていうつなぎ方があるのか。それなら勝ったも同然だな」と思ったのですが、記事にある関数宣言に従ったモジュールをyawsにくわせても、ちーっとも動かない。
で。しばらく考えて気がついた。
InfoQさんの記事は「out/3を自分で作るとRESTfulが作りやすくなるぜ」という意味だったのです。。。。
というわけで、とりあえずout/3を書いてみました。ちょろっと書くだけでmod_rewriteでごりごりやるようなRESTfulな処理が動くのはすごい便利。Yawsはぜひ使いこなしたいですね。
-module(sample_for_restful).
-include("/opt/local/lib/yaws/include/yaws_api.hrl").
-compile(export_all).
%% dispacher for RESTful service
out(A) ->
{http_request, Req, _params, _unknown} = A#arg.req,
Uri = yaws_api:request_url(A),
Path = string:tokens(Uri#url.path, "/"),
out(A, Req, Path).
%% sample for "GET http://localhost:8001/project/x/entities/123456"
out(A, 'GET', ["project", Project, "entities", EntityId]) ->
{ehtml,
[{p,[],
box(io_lib:format("GET for ~p~n"
"EntityId = ~p~n",
[Project,
EntityId]))}]};
%% sample for "catch all" handler.
out(A, _Method, _Params) ->
{ehtml,
[{p,[],
box(io_lib:format("general handler: A#arg.appmoddata = ~p~n"
"A#arg.appmod_prepath = ~p~n"
"A#arg.querydata = ~p~n",
[A#arg.appmoddata,
A#arg.appmod_prepath,
A#arg.querydata]))}]}.
%% utitity function in original sample.
box(Str) ->
{'div',[{class,"box"}],
{pre,[],Str}}.
このときのyaws.confはこんな感じ
<server erlangtestserver>
port = 8001
listen = 0.0.0.0
docroot = /somewhere/public_html
appmods = </project, sample_for_restful>
</server>
2009年08月06日
N人が参加する同期されたワールド、Javaでの分散キャッシュ、軽量化されたMySQL
古い記事だけど今になって重要になった。
読む!
梅田サロン中止のお詫び、およびアーキテクチャ変更についての技術詳細レポート
読む!
梅田サロン中止のお詫び、およびアーキテクチャ変更についての技術詳細レポート
すごく単純化して言えば、あるルームへの参加者N人中の1人あたりの平均的な信頼性を R (0
Java Caching SystemJCS is a distributed caching system written in java. It is intended to speed up applications by providing a means to manage cached data of various dynamic natures.
(Java Caching Systemは、Javaで記述された分散キャッシュシステムです。多様な動的なデータをキャッシュすることによってアプリケーションの高速化を実現します...ってな感じか)
ミクシィエンジニアブログ 2008/7Drizzleとは必要のないものは一切存在しない、最低限でパフォーマンス重視な「MySQLよりシンプルで、軽く、安定して、高速な」 MySQLのforkです。