โดย ทอม เพรสตัน-เวอร์เนอร์ ([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 หลายล้านรายการทุกวัน ความเสถียรและประสิทธิภาพเป็นแบบอย่าง
Ernie ติดตาม Semantic Versioning สำหรับเวอร์ชันรีลีส
ขั้นตอนที่ 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 แบบประ แต่ละเทอมคือรายการ 2 สิ่งอันดับที่ระบุตัวเลือกสำหรับโมดูล
แบบฟอร์มสำหรับโมดูลดั้งเดิมคือ:
[{module, Module},
{type, native},
{codepaths, CodePaths}].
โดยที่ Module คืออะตอมที่สอดคล้องกับชื่อโมดูลและ CodePaths คือรายการสตริงที่แสดงถึงเส้นทางของไฟล์ที่ควรเพิ่มลงในเส้นทางโค้ดของรันไทม์ เส้นทางเหล่านี้จะถูกเติมหน้าเส้นทางโค้ดและต้องมีไดเร็กทอรีของโมดูลดั้งเดิมและไดเร็กทอรีของการขึ้นต่อกันใด ๆ
แบบฟอร์มสำหรับโมดูลภายนอกคือ:
[{module, Module},
{type, external},
{command, Command},
{count, Count}].
โดยที่ Module คืออะตอมที่สอดคล้องกับชื่อโมดูล Command คือสตริงที่ระบุคำสั่งที่จะดำเนินการเพื่อเริ่มต้นผู้ปฏิบัติงาน และ Count คือจำนวนผู้ปฏิบัติงานที่จะวางไข่
หากคุณระบุโมดูลเนทีฟและโมดูลภายนอกที่มีชื่อเดียวกัน (และตามลำดับนั้น) เออร์นี่จะตรวจสอบโมดูลเนทิฟเพื่อดูว่ามีฟังก์ชันที่ร้องขอส่งออกหรือไม่ และใช้สิ่งนั้นหากเป็นเช่นนั้น หากไม่เป็นเช่นนั้น มันจะถอยกลับไปที่โมดูลภายนอก ซึ่งสามารถใช้เพื่อเพิ่มประสิทธิภาพการทำงานบางอย่างในโมดูลโดยไม่ต้องแก้ไขโค้ดไคลเอ็นต์ของคุณ
ในบางกรณี อาจเป็นการดีที่จะแชโดว์ฟังก์ชันในโมดูลภายนอกตามเงื่อนไขของธรรมชาติของอาร์กิวเมนต์ ตัวอย่างเช่น คุณอาจต้องการให้คำขอ math:fib(X)
ถูกกำหนดเส้นทางไปยังโมดูลภายนอกเมื่อ X น้อยกว่า 10 แต่ให้จัดการโดยโมดูลดั้งเดิมเมื่อ X เท่ากับ 10 หรือมากกว่า ซึ่งสามารถทำได้โดยการใช้ฟังก์ชัน math:fib_pred(X)
ในโมดูลดั้งเดิม สังเกตว่า _pred
ต่อท้ายชื่อฟังก์ชันปกติ (pred ย่อมาจากภาคแสดง) หากมีฟังก์ชันเช่นนี้ Ernie จะเรียกใช้ฟังก์ชันดังกล่าวพร้อมกับอาร์กิวเมนต์ที่ร้องขอ และหากค่าที่ส่งคืนเป็น true
ก็จะใช้โมดูลดั้งเดิม หากค่าที่ส่งคืนเป็น false
โมดูลภายนอกจะถูกนำมาใช้
ไฟล์กำหนดค่าตัวอย่างต่อไปนี้แจ้งให้ Ernie ทราบถึงสองโมดูล คำแรกระบุโมดูลดั้งเดิม '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 handlers ใน 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
Ernie รักษาคิวที่มีลำดับความสำคัญสูงและต่ำสำหรับการเชื่อมต่อขาเข้า หากมีการเชื่อมต่อใดๆ ในคิวที่มีลำดับความสำคัญสูง การเชื่อมต่อเหล่านั้นจะถูกประมวลผลก่อนเสมอ หากคิวที่มีลำดับความสำคัญสูงว่างเปล่า การเชื่อมต่อจะถูกประมวลผลจากคิวที่มีลำดับความสำคัญต่ำ ตามค่าเริ่มต้น การเชื่อมต่อจะเข้าสู่คิวที่มีลำดับความสำคัญสูง ในการเลือกคิว จะต้องส่งข้อมูล BERP ของแบบฟอร์มต่อไปนี้ก่อนการโทร
-- {info, priority, Priority}
โดยที่ Priority
คืออะตอม high
หรือ low
ลำดับตัวอย่างที่มีการเลือกคิวที่มีลำดับความสำคัญต่ำจะมีลักษณะดังต่อไปนี้
-> {info, priority, low}
-> {call, nat, add, [1, 2]}
<- {reply, 3}
คุณสามารถโทร BERT-RPC จาก Ruby ด้วย BERTRPC gem:
require 'bertrpc'
svc = BERTRPC::Service.new('localhost', 8000)
svc.call.ext.add(1, 2)
# => 3
หากคุณต้องการแฮ็ก Ernie ให้เริ่มต้นด้วยการฟอร์ก repo ของฉันบน GitHub:
http://github.com/mojombo/ernie
หากต้องการได้รับการอ้างอิงทั้งหมด ให้ติดตั้งอัญมณีก่อน หากต้องการเรียกใช้ ernie จากแหล่งที่มา คุณต้องสร้างโค้ด Erlang ก่อน:
rake ebuild
วิธีที่ดีที่สุดในการรวมการเปลี่ยนแปลงกลับเข้าสู่แกนหลักมีดังนี้:
rake
ลิขสิทธิ์ (c) 2009 ทอม เพรสตัน-เวอร์เนอร์ ดูใบอนุญาตสำหรับรายละเอียด