-
Notifications
You must be signed in to change notification settings - Fork 1
Introduction to ECell
ECell is a framework built on Celluloid and ØMQ for building concurrent, networked, service-based systems.
ECell systems are built out of services called Pieces, which occupy entire Ruby processes (a Piece works with process-global state; this may or may not be changed). Each Piece has one Subject, which is its "primary" actor. Subjects instantiate the class Subject
, which includes a state machine governing the life cycle of the Piece. We define a type of Piece we can run by writing a subclass of Subject
; such a subclass is called a Sketch. Here's a simple Sketch:
require 'ecell/elements/subject'
require 'ecell/base/designs/follower'
require 'ecell/base/designs/answerer'
class Adder < ECell::Elements::Subject
def initialize(configuration={})
design! ECell::Base::Designs::Follower,
ECell::Base::Designs::Answerer
super(configuration)
end
module RPC
def add(x, y)
x + y
end
end
include RPC
end
This Piece makes use of the Follower
and Answerer
Designs. A Design is a reusable bundle of functionality that extends Pieces with some kind of behavior or ability. Designs can do a number of things, including spawning auxiliary actors called Figures, making connections and bindings, adding methods to Subjects, and registering callbacks for various events. The Follower
Design extends a Piece to follow a mesh leader, allowing it to be monitored, managed, and giving it the ability to log to the leader's centralized log. The Answerer
Design extends the Piece to answer RPCs. It serves any methods on the Subject that were defined in the Sketch's RPC
module.
This Sketch can be run using the ECell::Run.run!
method as follows (assuming that monitor
is the ID of a mesh leader Piece):
require 'ecell/run'
bindings = {monitor: {}} # this should be filled with the interface and ports that the monitor Piece is using
ECell::Run.run! Adder, piece_id: :adder, bindings: bindings, leader: :monitor
This will bring an instance of the Answerer
sketch online, ready to answer calls.
Here is an example of a self-contained, working mesh:
#!/usr/bin/env ruby
require 'ecell/elements/subject'
require 'ecell/base/designs/follower'
require 'ecell/base/designs/answerer'
require 'ecell/base/designs/caller'
require 'ecell/base/sketches/monitor' # a basic leader sketch
require 'ecell/run'
class Adder < ECell::Elements::Subject
def initialize(configuration={})
design! ECell::Base::Designs::Follower,
ECell::Base::Designs::Answerer
super(configuration)
end
module RPC
def add(x, y)
x + y
end
end
include RPC
end
class Fib < ECell::Elements::Subject
def initialize(configuration={})
design! ECell::Base::Designs::Follower,
ECell::Base::Designs::Caller
super(configuration)
@adder_id = configuration[:adder_id]
end
def at_running # hook called by the state machine governing the Piece
super
a, b = 0, 1
every(1) do # Celluloid `every` method
# synchronously place a call to the piece with the id `@adder_id`
answer = ECell.call_sync(@adder_id).add(a, b)
a, b = b, answer.returns
info(message: a)
end
end
end
monitor_base = 7000
bindings = {
monitor: {
interface: "127.0.0.1", # put an externally accessible IP if you want to run each Piece on a separate machine
awareness_subscribe: monitor_base,
logging_pull: monitor_base += 1,
management_router: monitor_base += 1,
management_publish: monitor_base += 1,
calling_router2: monitor_base += 1,
calling_router: monitor_base += 1
}
}
# each piece connects to the leader, so it can route calls
# without the pieces needing to make pre-known bindings,
# so they don't need to be configured here.
log_dir = File.expand_path("../logs", __FILE__)
basic_conf = {bindings: bindings, leader: :monitor, log_dir: log_dir}
case ARGV[0]
when "monitor"
ECell::Run.run! ECell::Base::Sketches::Monitor,
basic_conf.merge(piece_id: :monitor, followers: [:adder, :fib])
when "adder"
ECell::Run.run! Adder,
basic_conf.merge(piece_id: :adder)
when "fib"
ECell::Run.run! Fib,
basic_conf.merge(piece_id: :fib, adder_id: :adder)
end
Save this as mesh.rb
and then make a logs
directory next to it. If your current ruby
command is supplied by Rubinius, you should be able to just run ./mesh.rb monitor
, ./mesh.rb adder
, and ./mesh.rb fib
(in no particular order, as long as they're all started reasonably close to one another), even on different machines from each other. Check the logs for monitor
- they should contain the expected periodic messages from fib
.