Том Престон-Вернер ([email protected])
Ernie — это реализация сервера BERT-RPC, которая использует сервер Erlang для приема входящих соединений, а затем делегирует запрос пользовательским модулям, которые вы можете написать на любом языке (в настоящее время включена поддержка только Ruby и Erlang).
Модули, написанные на Ruby или любом другом языке, отличном от Erlang, известны как «внешние» модули, и вы должны указать, сколько рабочих процессов каждого модуля должно быть создано. Запросы к этим модулям распределяются между воркёрами. Модули, написанные на Erlang, называются «родными» модулями и запускаются во время выполнения сервера Erlang. Поскольку они создаются как упрощенные процессы, нет необходимости в балансировке и гораздо меньше накладных расходов на связь по сравнению с внешними модулями.
Ernie поддерживает несколько гетерогенных модулей. Например, у вас может быть внешний модуль Ruby, на котором одновременно работают 10 рабочих процессов , и собственный модуль Erlang. Эрни отслеживает отправку запросов соответствующему модулю. Используя технику, называемую «затенением», вы можете выборочно оптимизировать определенные функции внешнего модуля с помощью собственного кода, а Эрни сам выберет правильную функцию.
Полную спецификацию BERT-RPC см. на сайте bert-rpc.org.
В настоящее время Ernie поддерживает следующие функции BERT-RPC:
call
cast
запросыErnie был разработан для GitHub и в настоящее время используется в производстве, обслуживая миллионы запросов RPC каждый день. Стабильность и производительность были образцовыми.
Эрни использует семантическое управление версиями для управления версиями релизов.
Шаг 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 проверит собственный модуль, чтобы узнать, экспортирована ли в него запрошенная функция, и воспользуется ею, если она есть. Если этого не произойдет, то он вернется к внешнему модулю. Это можно использовать для выборочной оптимизации определенных функций в модуле без каких-либо изменений в клиентском коде.
В некоторых случаях может быть полезно условно дублировать функцию во внешнем модуле в зависимости от характера аргументов. Например, вы можете захотеть, чтобы запросы math:fib(X)
направлялись во внешний модуль, когда X меньше 10, но обрабатывались собственным модулем, когда X равно 10 или больше. Этого можно добиться, реализовав функцию math:fib_pred(X)
в собственном модуле. Обратите внимание на _pred
, добавленный к обычному имени функции (pred — это сокращение от слова «предикат»). Если такая функция присутствует, Эрни вызовет ее с запрошенными аргументами, и если возвращаемое значение true
будет использован собственный модуль. Если возвращаемое значение false
будет использоваться внешний модуль.
Следующий пример файла конфигурации информирует Эрни о двух модулях. Первый термин идентифицирует собственный модуль «nat», который находится в файле nat.beam в каталоге «/path/to/app/ebin». Второй термин указывает внешний модуль «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}
В этот драгоценный камень включена библиотека ernie
, которая позволяет легко писать обработчики Ernie на Ruby. Все, что вам нужно сделать, это написать стандартный модуль 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
Эрни поддерживает очереди с высоким и низким приоритетом для входящих соединений. Если в очереди с высоким приоритетом есть какие-либо соединения, они всегда будут обрабатываться в первую очередь. Если очередь с высоким приоритетом пуста, соединения будут обрабатываться из очереди с низким приоритетом. По умолчанию соединения попадают в очередь с высоким приоритетом. Чтобы выбрать очередь, перед вызовом необходимо отправить информационное сообщение BERP следующей формы.
-- {info, priority, Priority}
Где Priority
является либо high
, либо low
атом. Пример последовательности, в которой выбирается очередь с низким приоритетом, будет выглядеть следующим образом.
-> {info, priority, low}
-> {call, nat, add, [1, 2]}
<- {reply, 3}
Вы можете выполнять вызовы BERT-RPC из Ruby с помощью драгоценного камня BERTRPC:
require 'bertrpc'
svc = BERTRPC::Service.new('localhost', 8000)
svc.call.ext.add(1, 2)
# => 3
Если вы хотите взломать Ernie, начните с создания моего репозитория на GitHub:
http://github.com/mojombo/ernie
Чтобы получить все зависимости, сначала установите драгоценный камень. Чтобы запустить ernie из исходного кода, вы должны сначала собрать код Erlang:
rake ebuild
Лучший способ объединить ваши изменения обратно в ядро заключается в следующем:
rake
Авторские права (c) 2009 Том Престон-Вернер. Подробности см. в разделе ЛИЦЕНЗИЯ.