トム・プレストン・ワーナー著 ([email protected])
Ernie は、Erlang サーバーを使用して受信接続を受け入れ、任意の言語で作成できるカスタム モジュールにリクエストを委任する BERT-RPC サーバー実装です (現在、Ruby と Erlang のサポートのみが含まれています)。
Ruby または Erlang 以外の言語で書かれたモジュールは「外部」モジュールとして知られており、各モジュールのワーカーをいくつ生成するかを指定する必要があります。これらのモジュールに対するリクエストはワーカー間でバランスがとれます。 Erlang で書かれたモジュールは「ネイティブ」モジュールとして知られ、Erlang サーバーのランタイム内で実行されます。これらは軽量プロセスとして生成されるため、バランスをとる必要がなく、外部モジュールと比較して通信オーバーヘッドがはるかに少なくなります。
Ernie は複数の異種モジュールをサポートします。たとえば、10 個のワーカーを実行する外部 Ruby モジュールと、ネイティブ Erlang モジュールを同時に実行することができます。 Ernie は、適切なモジュールへのリクエストの送信を追跡します。 「シャドウイング」と呼ばれる手法を使用すると、ネイティブ コードで特定の外部モジュール関数を選択的に最適化でき、Ernie が適切な関数の選択を処理します。
BERT-RPC の完全な仕様については、bert-rpc.org を参照してください。
Ernie は現在、次の BERT-RPC 機能をサポートしています。
call
リクエストcast
リクエストErnie は GitHub 用に開発され、現在実稼働環境で毎日数百万の RPC リクエストに対応しています。安定性とパフォーマンスは模範的でした。
Ernie は、リリースのバージョン管理のためにセマンティック バージョニングに従います。
ステップ 1: Erlang (R13B 以降) をインストールします。
http://www.erlang.org/download.html
ステップ 2: Ernie をインストールします。
$ [sudo] gem install ernie
Usage: ernie [command] [options]
-c, --config CONFIG Config file.
-p, --port PORT Port.
-l, --log-level Log level (0-4).
-a, --access-log LOGFILE Access log file.
-d, --detached Run as a daemon.
-P, --pidfile PIDFILE Location to write pid file.
--name NAME Erlang process name.
--sname SNAME Erlang short process name.
-E, --erlang ERLANG_OPTIONS Options passed to Erlang VM.
Commands:
Start an Ernie server.
reload-handlers Gracefully reload all of the external handlers
and use the new code for all subsequent requests.
stats Print a list of connection and handler statistics.
Examples:
ernie -d -p 9999 -c example.cfg
Start the ernie server in the background on port 9999 using the
example.cfg configuration file.
ernie reload-handlers -p 9999
Reload the handlers for the ernie server currently running on
port 9999.
ernie -c example.cfg -E '-run mymodule'
Start the ernie server with an additional erlang module called
'mymodule'
Ernie 設定ファイルは、一連の点線で区切られた Erlang 用語として記述されます。各用語は、モジュールのオプションを指定する 2 つのタプルのリストです。
ネイティブ モジュールの形式は次のとおりです。
[{module, Module},
{type, native},
{codepaths, CodePaths}].
ここで、Module はモジュール名に対応するアトムで、CodePaths はランタイムのコード パスに追加する必要があるファイル パスを表す文字列のリストです。これらのパスはコード パスの先頭に追加され、ネイティブ モジュールのディレクトリと依存関係のディレクトリを含める必要があります。
外部モジュールの形式は次のとおりです。
[{module, Module},
{type, external},
{command, Command},
{count, Count}].
ここで、Module はモジュール名に対応するアトム、Command はワーカーを開始するために実行するコマンドを指定する文字列、Count は生成するワーカーの数です。
同じ名前のネイティブ モジュールと外部モジュールを (その順序で) 指定すると、Ernie はネイティブ モジュールを検査して、要求された関数がエクスポートされているかどうかを確認し、エクスポートされている場合はそれを使用します。そうでない場合は、外部モジュールにフォールバックします。これを使用すると、クライアント コードを変更せずに、モジュール内の特定の関数を選択的に最適化できます。
状況によっては、引数の性質に基づいて外部モジュール内の関数を条件付きでシャドウすると便利な場合があります。たとえば、X が 10 未満の場合はmath:fib(X)
のリクエストを外部モジュールにルーティングし、X が 10 以上の場合はネイティブ モジュールで処理するようにすることができます。これは、関数math:fib_pred(X)
ネイティブ モジュールに実装することで実現できます。通常の関数名に_pred
が追加されていることに注目してください (pred は述語の略です)。このような関数が存在する場合、アーニーは要求された引数を使用してそれを呼び出し、戻り値がtrue
の場合はネイティブ モジュールが使用されます。戻り値がfalse
の場合、外部モジュールが使用されます。
次の構成ファイルの例は、Ernie に 2 つのモジュールを通知します。最初の用語は、「/path/to/app/ebin」ディレクトリの下の nat.beam ファイルに存在するネイティブ モジュール「nat」を識別します。 2 番目の項は、コマンド「ruby /path/to/app/ernie/ext.rb」で開始される 2 つのワーカーを持つ外部モジュール「ext」を指定します。
[{module, nat},
{type, native},
{codepaths, ["/path/to/app/ebin"]}].
[{module, ext},
{type, external},
{command, "ruby /path/to/app/ernie/ext.rb"},
{count, 2}].
アクセス ログの書き込みをリクエストした場合 (-a または --access-log オプションを使用)、すべてのリクエストがそのファイルに記録されます。各リクエストは 1 行に出力されます。ログ行の要素は次のとおりです (右側にコメントがあります)。
ACC type of message [ ACC | ERR ]
[2010-02-20T11:42:25.259750] time the connection was accepted
0.000053 seconds from connection to processing start
0.000237 seconds from processing start to finish
- delimiter
0 size of high queue at connect time
0 size of low queue at connect time
nat type of handler [ nat | ext ]
high priority [ high | low ]
- delimiter
{call,nat,add,[1,2]} message
ログ行はリクエストの完了時に書き込まれるため、接続時間に関して順序が正しくない場合があります。ログのローテーションを容易にするために、現在のログ ファイルが移動または削除された場合、Ernie は新しいアクセス ログ ファイルを作成します。
ネイティブ ハンドラーは通常の Erlang モジュールとして記述されます。エクスポートされた関数は BERT-RPC クライアントで使用できるようになります。
-module(nat).
-export([add/2]).
add(A, B) ->
A + B.
-> {call, nat, add, [1, 2]}
<- {reply, 3}
この gem には、Ruby で Ernie ハンドラーを簡単に作成できるernie
というライブラリが含まれています。標準の Ruby モジュールを作成して Ernie に公開するだけで、そのモジュールの機能が BERT-RPC クライアントで利用できるようになります。
Ruby モジュールと Ernie.expose を使用する:
require 'rubygems'
require 'ernie'
module Ext
def add(a, b)
a + b
end
end
Ernie.expose(:ext, Ext)
-> {call, nat, add, [1, 2]}
<- {reply, 3}
次の行をハンドラーに追加すると、ログをファイルに送信できます。
logfile('/var/log/ernie.log')
loglevel(Logger::INFO)
これにより、起動情報、リクエスト、エラー メッセージがログに記録されます。 Logger::DEBUG を選択すると、応答が含まれます (これを選択すると、非常に大きなログ ファイルが生成される可能性があるので注意してください)。
通常、Ruby Ernie ハンドラーは、ファイルがロードされた後にアクティブになります。次のように設定することで、この動作を無効にできます。
Ernie.auto_start = false
Ernie は、受信接続用に高優先度キューと低優先度キューを維持します。高優先キューに接続がある場合、それらの接続が常に最初に処理されます。高優先度キューが空の場合、接続は低優先度キューから処理されます。デフォルトでは、接続は高優先度キューに入ります。キューを選択するには、呼び出しの前に次の形式の情報 BERP を送信する必要があります。
-- {info, priority, Priority}
ここで、 Priority
はhigh
またはlow
アトムのいずれかです。低優先度のキューが選択されるシーケンスの例は次のようになります。
-> {info, priority, low}
-> {call, nat, add, [1, 2]}
<- {reply, 3}
BERTRPC gem を使用すると、Ruby から BERT-RPC 呼び出しを行うことができます。
require 'bertrpc'
svc = BERTRPC::Service.new('localhost', 8000)
svc.call.ext.add(1, 2)
# => 3
Ernie をハッキングしたい場合は、GitHub 上の私のリポジトリをフォークすることから始めます。
http://github.com/mojombo/ernie
すべての依存関係を取得するには、最初に gem をインストールします。 ernie をソースから実行するには、まず Erlang コードをビルドする必要があります。
rake ebuild
変更をコアにマージして戻す最良の方法は次のとおりです。
rake
実行してすべてが合格することを確認します著作権 (c) 2009 トム プレストン ワーナー。詳細については、「ライセンス」を参照してください。