بقلم توم بريستون-فيرنر ([email protected])
Ernie هو تطبيق خادم BERT-RPC يستخدم خادم Erlang لقبول الاتصالات الواردة، ثم يقوم بتفويض الطلب إلى وحدات نمطية مخصصة يمكنك كتابتها بأي لغة (حاليًا يتم تضمين دعم Ruby وErlang فقط).
تُعرف الوحدات المكتوبة بلغة Ruby أو أي لغة غير لغة Erlang بالوحدات النمطية "الخارجية" ويجب عليك تحديد عدد العاملين في كل وحدة يجب إنتاجها. تتم موازنة الطلبات المقدمة ضد هذه الوحدات بين العاملين. تُعرف الوحدات المكتوبة بلغة Erlang بالوحدات النمطية "الأصلية" ويتم تشغيلها خلال وقت تشغيل خادم Erlang. نظرًا لأن هذه العمليات يتم إنتاجها كعمليات خفيفة الوزن، فلا يوجد موازنة ضرورية وتكاليف اتصال أقل بكثير عند مقارنتها بالوحدات الخارجية.
يدعم Ernie وحدات متعددة غير متجانسة. على سبيل المثال، يمكن أن يكون لديك وحدة Ruby خارجية تعمل على تشغيل 10 عمال ووحدة Erlang أصلية تعمل في وقت واحد. يتتبع Ernie إرسال الطلبات إلى الوحدة المناسبة. باستخدام تقنية تسمى "التظليل"، يمكنك تحسين بعض وظائف الوحدة الخارجية بشكل انتقائي باستخدام التعليمات البرمجية الأصلية وسيتولى Ernie اختيار الوظيفة الصحيحة.
راجع مواصفات 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}].
حيث الوحدة النمطية هي ذرة تتوافق مع اسم الوحدة النمطية وCodePaths هي قائمة من السلاسل التي تمثل مسارات الملفات التي يجب إضافتها إلى مسار التعليمات البرمجية في وقت التشغيل. سيتم إضافة هذه المسارات مسبقًا إلى مسار التعليمات البرمجية ويجب أن تتضمن دليل الوحدة الأصلية وأدلة أي تبعيات.
نموذج الوحدات الخارجية هو:
[{module, Module},
{type, external},
{command, Command},
{count, Count}].
حيث الوحدة هي ذرة تتوافق مع اسم الوحدة، والأمر عبارة عن سلسلة تحدد الأمر الذي سيتم تنفيذه لبدء عامل، والعدد هو عدد العمال الذين سيتم نشرهم.
إذا قمت بتحديد وحدة أصلية ووحدة خارجية بنفس الاسم (وبهذا الترتيب)، فسوف يقوم Ernie بفحص الوحدة الأصلية لمعرفة ما إذا كانت تحتوي على الوظيفة المطلوبة المُصدَّرة ويستخدمها إذا كانت كذلك. إذا لم يحدث ذلك، فسوف يعود إلى الوحدة الخارجية. يمكن استخدام هذا لتحسين وظائف معينة بشكل انتقائي في الوحدة دون أي تعديلات على كود العميل الخاص بك.
في بعض الحالات، قد يكون من الجيد تظليل دالة بشكل مشروط في وحدة نمطية خارجية بناءً على طبيعة الوسائط. على سبيل المثال، قد ترغب في توجيه طلبات math:fib(X)
إلى الوحدة الخارجية عندما تكون X أقل من 10، ولكن تتم معالجتها بواسطة الوحدة الأصلية عندما تكون X 10 أو أكبر. يمكن تحقيق ذلك من خلال تنفيذ دالة math:fib_pred(X)
في الوحدة الأصلية. لاحظ _pred
الملحق باسم الوظيفة العادية (pred هو اختصار للمسند). في حالة وجود دالة كهذه، فسيستدعيها Ernie باستخدام الوسائط المطلوبة وإذا كانت القيمة المرجعة true
فسيتم استخدام الوحدة الأصلية. إذا كانت قيمة الإرجاع false
فسيتم استخدام الوحدة الخارجية.
يُعلم ملف التكوين المثال التالي إرني بوجود وحدتين. يحدد المصطلح الأول الوحدة النمطية الأصلية "nat" الموجودة في ملف nat.beam ضمن الدليل "/path/to/app/ebin". يحدد المصطلح الثاني وحدة خارجية "ext" والتي ستحتوي على عاملين يبدأان بالأمر "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 القياسية وعرضها على Ernie وستصبح وظائف تلك الوحدة متاحة لعملاء BERT-RPC.
باستخدام وحدة روبي و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}
يمكنك إجراء مكالمات 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
للحصول على كافة التبعيات، قم بتثبيت الجوهرة أولاً. لتشغيل إرني من المصدر، يجب عليك أولاً إنشاء كود Erlang:
rake ebuild
أفضل طريقة لدمج تغييراتك مرة أخرى في المركز هي كما يلي:
rake
حقوق الطبع والنشر (ج) 2009 توم بريستون فيرنر. راجع الترخيص للحصول على التفاصيل.