作者:湯姆‧普雷斯頓-沃納 ([email protected])
Ernie 是一個 BERT-RPC 伺服器實現,它使用 Erlang 伺服器接受傳入連接,然後將請求委託給可以用任何語言編寫的自訂模組(目前僅包括 Ruby 和 Erlang 支援)。
用 Ruby 或任何非 Erlang 語言編寫的模組稱為「外部」模組,您必須指定應產生每個模組的工作進程數量。針對這些模組的請求在工作人員之間進行平衡。用 Erlang 編寫的模組稱為「本機」模組,並在 Erlang 伺服器的執行時間內運作。由於這些是作為輕量級進程產生的,因此與外部模組相比,不需要平衡,並且通訊開銷要少得多。
Ernie 支援多個異質模組。例如,您可以讓一個執行 10 個工作進程的外部 Ruby 模組和一個同時執行的本機 Erlang 模組。 Ernie 追蹤將請求傳送到正確的模組。使用一種稱為「影子」的技術,您可以使用本機程式碼選擇性地最佳化某些外部模組函數,Ernie 將負責選擇正確的函數。
請參閱 bert-rpc.org 上的完整 BERT-RPC 規格。
Ernie 目前支援以下 BERT-RPC 功能:
call
請求cast
要求Ernie 是為 GitHub 開發的,目前在生產環境中每天為數百萬個 RPC 請求提供服務。穩定性和性能堪稱典範。
Ernie 遵循語意版本控制來進行發布版本控制。
第 1 步:安裝 Erlang(R13B 或更高版本)。
http://www.erlang.org/download.html
第 2 步:安裝厄尼:
$ [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 術語。每個術語都是指定模組選項的二元組列表。
本機模組的形式是:
[{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 是 predicate 的縮寫)。如果存在這樣的函數,Ernie 將使用請求的參數來呼叫它,如果傳回值為true
則將使用本機模組。如果傳回值為false
則將使用外部模組。
以下範例設定檔告知 Ernie 有兩個模組。第一項標識駐留在「/path/to/app/ebin」目錄下的 nat.beam 檔案中的本機模組「nat」。第二項指定一個外部模組“ext”,該模組將有 2 個工作程序以命令“ruby /path/to/app/ernie/ext.rb”啟動。
[{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 選項),則所有請求都會記錄到該檔案中。每個請求都列印在一行上。日誌行的元素如下(右側有註解):
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 中包含一個名為ernie
的函式庫,它可以讓您輕鬆地用 Ruby 編寫 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 湯姆普雷斯頓-沃納。有關詳細信息,請參閱許可證。