Distributed programming with Jabber and EventMachine
Jabber and its underlying protocol XMPP are typically associated with instant messaging applications, although the breadth and flexibility of the technology allows for implementations that can span further from traditional online chatting.
ejabberd is a fault tolerant and clusterable Jabber/XMPP server written in Erlang and presents an interesting option as a simple, lightweight and scalable message transport for distributed applications.
EventMachine is a simple and fast library for lightweight concurrency in Ruby. Its use mainly involves, but is not limited to, spawning lightweight processes whose execution can be programatically scheduled, easy and fast socket abstractions and an implementation of the Deferrable pattern as introduced by the Twisted event-driven Python networking engine.
When a Ruby class includes the
EventMachine::Deferrable
module, it is provided with the ability to accept arbitrary callbacks and errbacks that will get executed when its deferred status changes, in particular when it is set to either
:succeeded
or
:failed.
Let's look at a deferrable
Worker
class which performs a potentially long running operation.
class Worker
include EM::Deferrable
def heavy_lifting
30.times do |i|
puts "Lifted #{i}"
sleep 0.1
end
set_deferred_status :succeeded
end
end
Inside an EventMachine loop, we can add callbacks to a
Worker
instance and dispatch the expensive operation to a separate thread, or an evented process. The program's execution will continue, with any callbacks attached to
Worker
executed once its deferred status is set.
EM.run do
worker = Worker.new
worker.callback {p "done!"}
Thread.new {worker.heavy_lifting; EM.stop}
puts "resuming remaining program operations"
end
Now, let's look at combining
Worker
with Jabber to trigger long running jobs. For Jabber server duties, I am using ejabberd on an old laptop running Debian, but there's no reason why a mass online Jabber service like
Google Talk
could not be used for playing around with the example. Also, I'm using the
xmpp4r-simple
Ruby library, which is a wrapper around
xmpp4r.
jabber = Jabber::Simple.new("bot@thrash", "password")
at_exit{jabber.status(:away, "jabot down")}
EM.run do
EM::PeriodicTimer.new(1) do
jabber.received_messages do |message|
case message.body
when "exit" : EM.stop
when "lift" :
EM.spawn do
worker = Worker.new
worker.callback {jabber.deliver(message.from, "Done lifting")}
worker.heavy_lifting
end.notify
jabber.deliver(message.from, "Scheduled heavy job...")
else jabber.deliver(message.from, "Dunno how to #{message.body}")
end
end
end
end
Inside an EventMachine loop, we check for new messages every second. The program understands two commands,
exit
and
lift.
The first quits the EventMachine loop and ultimately terminates the program's execution. When
lift
is received, we instantiate a new
Worker
inside a spawned process and add a callback so that the
Worker
will notify the command issuer when the job has completed. Worth noting is the use of
notify
to schedule the spawned process.
notify
returns immediately making work dispatch non-blocking - upon issuing a
lift
command twice, a "Scheduled heavy job..." message will be sent to the job issuer twice before the first job completes.
I use Adium to send commands to the program - an interesting way of remote controlling or interacting with applications. Of course, the real interest lies in using the setup under discussion for inter-app communication. With multicast options, presence discovery, node status updates and more, there is lot to explore in terms of distributed application development, if simple and lightweight are two keywords to be found on the highest ranks of your list.
Message from Testkings
Guaranteed pass you real exam with testking VCP-410 course. Sign up for testking 350-001 online training program and get guaranteed success using self study testking 640-802 preparation material.