1. Computing
Send to a Friend via Email

An Introduction to Distributed Ruby

Network Communication with DRb

By

With Distributed Ruby, or DRb, you'll never have, as Paul Newman so aptly put it in Cool Hand Luke, "a failure to communicate." That's because connecting Ruby programs over a network is trivial business with the DRb library. Since Ruby's objects pass "messages" instead of doing function calls (as in Java or C++), extending this to work over the network is natural. The DRb library provides a way to share objects over the network, allowing Ruby clients to easily connect to them and use those objects as if they are part of the native program.

The Counter Object

To begin our introduction to DRb, we'll need to have an example object to share. Here, the object to be shared over the network is a counter object, like that which would be found keeping track of the number of visitors to a website. You can do two things to the counter object: increment its value by one and read its value. The example program is as simple as possible. though more complex objects are just as easily shared across networks.

 #!/usr/bin/env ruby
 
 class Counter
   attr_reader :value
 
   def initialize
     @value = 0
   end
 
   def increment
     @value += 1
   end
 end
 

The Server

To use DRb as either a client or server, you'll need to start by requiring the drb file. Starting a DRb server can be accomplished by running the DRB.start_service method. This method takes two arguments: the URI to on which to run the server (which includes the TCP port) and the object to be shared over the network.

A URI is similar, but not exactly the same as a URL. While both are Uniform Resource locators, if you think of the two as elements of a street map, the URL is the address, while the URI is more like the name of the person or family who lives at the address. Note that if you don't have a preference as to on what port the server runs, simply pass nil for the first argument and DRb will automatically generate a URI. Be sure to print that URI to the screen or a file, or else the clients won't know where to connect.

The DRB.start_service method creates threads that run in the background. This method will return immediately and your program will continue executing. In order to wait for the server to finish (or wait indefinitely if the server has no way of exiting), you have to "join" the server thread. In thread lingo, "joining" a thread is to wait for it to finish before continuing.

There's just one more piece of housekeeping: handling an interrupt correctly. If your server is running on a terminal and you press Ctrl-C or close the terminal, your script will receive an interrupt. This interrupt must be handled in order to allow DRb to cleanly inform the clients that the server is shutting down. Here's how to do that:

 #!/usr/bin/env ruby
 require 'drb'
 
 class Counter
   attr_reader :value
 
   def initialize
     @value = 0
   end
 
   def increment
     @value += 1
   end
 end
 
 DRb.start_service 'druby://:9000', Counter.new
 puts "Server running at #{DRb.uri}"
 
 trap("INT") { DRb.stop_service }
 DRb.thread.join
 

The Client

Whereas other RPC protocols such as XML-RPC or CORBA rely on "interface definition languages" (IDL) to define what messages the client is allowed to send to the server (and what it expects to be returned by the server), DRb works a little differently. The client doesn't have to know anything about the object being shared by the server.

When you call a method on a normal Ruby object, what you're actually doing is "sending a message" to that object. That object then interprets the message, looks at its list of methods and--if it sees that it has a method with the same name as the message--calls the method. In this scenario, the caller of a method doesn't need to know anything about the callee.

The same is true for objects shared by DRb over a network. The client doesn't know anything about the object being shared; it will simply send messages to the shared object and wait for a response. The code using the DRb shared object will have no idea it's shared over the network and that it's not a "real" live object in the program.

The client works a lot like the server. You must first require drb, then create a new DRbObject. When you create a new DRbObject, you must pass the URI of the server to the constructor. Once you've done all of that, the DRbObject can then be used by the client as if the shared object were running in the client program.

 #!/usr/bin/env ruby
 require 'drb'
 
 counter = DRbObject.new nil, 'druby://:9000'
 
 counter.increment
 puts "The counter value is #{counter.value}"
 

©2014 About.com. All rights reserved.