1.1 Hello, World
Let’s create a server that prints out strings. Then we’ll code a simple client and use it to make the server print “Hello, World.” The client and server will each run in a separate process (and to make that easy, we’ll run each process from a separate terminal window).
Creating the Printing Server
puts00.rb
is the puts
server.
puts00.rb | |
Line 1 | require 'drb/drb' |
- | class Puts |
- | def initialize(stream=$stdout) |
- | @stream = stream |
5 | end |
- | |
- | def puts(str) |
- | @stream.puts(str) |
- | end |
10 | end |
- | uri = ARGV.shift |
- | DRb.start_service(uri, Puts.new) |
- | puts DRb.uri |
- | DRb.thread.join() |
Let’s go through the script:
-
On line 1, we require the
drb
library. -
We create a class called
Puts
on line 2. This class contains theputs
method that we’ll make available to the client. -
On line 12, we start the dRuby service. We provide the URI (which the user passes in on the command line). The URL is the address the client uses to connect to the server. We also provide the object that will be tied to the URI. You’ll find out more about the URI in The dRuby URI, Services, and Clients.
-
A dRuby service runs in a separate thread. One of the most common mistakes new dRuby programmers make is to forget that their program will simply exit unless they make sure to wait until the thread stops executing. On line 14, we use
DRb.thread.join
to keep the script up and running.
We’re going to use one terminal window to run the server. Let’s call it terminal 1. In that window, run puts00.rb
, passing it the URI of the service.
# [Terminal 1] |
|
% ruby puts00.rb druby://localhost:12345 |
|
druby://localhost:12345 |
The server process waits for the request to arrive. Make sure that the server doesn’t terminate, even after it prints out the URI of the service.
Using the Service from irb
The next step is to write the client. Rather than writing a program file, we’ll just use irb. Open another terminal (terminal 2) and type the following:
# [Terminal 2] |
|
% irb |
|
irb(main):001:0> require 'drb/drb' |
|
=> true |
|
irb(main):002:0> there = DRbObject.new_with_uri('druby://localhost:12345') |
|
=> #<DRb::DRbObject: ... > |
We start by requiring the drb
library—the client and the server both need it. We then create a dRuby object (of class DRbObject
) by calling DRbObject.new_with_uri
(refer to OS X and readline if you encounter a problem getting the prompt back), passing it the same URI we used when creating the server. We store this object in the variable there
.
Now we can use this dRuby object to access methods on the server. It’s as if the client has access to the Puts
object we created on the server.
irb(main):003:0> there.puts('Hello, World.') |
|
=> nil |
We called the puts
method of the Puts
server (see Figure 1, Puts server and irb client). You should see “Hello, World.” printed on terminal 1 where the server is running.
% ruby puts00.rb druby://localhost:12345 |
|
druby://localhost:12345 |
|
Hello, World. |
That’s pretty cool. We needed only a few lines of code to create a simple distributed server.
If you didn’t notice any difference, try other characters. Make sure you observe the server terminal while you are typing in irb.
Back in irb on terminal 2, let’s call the server again.
# [Terminal 2] |
|
irb(main):004:0> there.puts('R is for Ruby.') |
|
=> nil |
You should see the second message appear on terminal 1.
The there
variable in the client refers to the Puts
service object. By sending the puts
method to the there
variable, you invoke the puts
method in the server, and it prints the object you pass to standard output.
What happens if you stop the server? Try it—type Ctrl-C on terminal 1 and make sure you get back to a command prompt.
Now, back on terminal 2, call there.puts
again.
# [Terminal 2] |
|
irb(main):005:0> there.puts('Hello, again.') |
|
DRb::DRbConnError: druby://localhost:12345 - #<Errno::ECONNREFUSED.... |
dRuby raised an exception. DRbConnError
means that there is a communication error between dRuby processes. The client failed to invoke the method because the server is stopped.
Let’s start the server again in terminal 1.
# [Terminal 1] |
|
% ruby puts00.rb druby://localhost:12345 |
|
druby://localhost:12345 |
Try there.puts
again.
# [Terminal 2] |
|
irb(main):006:0> there.puts('Hello, again.') |
|
=> nil |
This time, there is no exception, and you should see “Hello, again.” printed on terminal 1.
# [Terminal 1] |
|
% ruby puts00.rb |
|
druby://localhost:12345 |
|
Hello, again. |
Let’s stop irb for now.
# [Terminal 2] |
|
irb(main):007:0> exit |
Creating the Script Version of the Client
As a final “Hello, World” experiment, let’s rewrite this as a script.
hello00.rb | |
require 'drb/drb' |
|
|
|
uri = ARGV.shift |
|
there = DRbObject.new_with_uri(uri) |
|
there.puts('Hello, World.') |
As you can see, this script contains most of the same code that you typed into irb, except it gets the URI from the command line. Let’s try it. Run hello00.rb
in terminal 2.
# [Terminal 2] |
|
% ruby hello00.rb druby://localhost:12345 |
You should see “Hello, World” appear on terminal 1.
So far, we’ve experimented with a simple dRuby example, and we’ve seen how easy it is to write a client-server model script. It’s time to go a little further.
The dRuby URI, Services, and Clients
In the previous example, we used druby://localhost:12345
as a URI, but we haven’t seen what this actually means. In this section, we’ll learn about the relationship between the dRuby URI and the URI specified in DRb.start_service
.
A dRuby URI defines the path to a dRuby server. It consists of the protocol (always druby
), an optional hostname, and an optional port number.
druby://[hostname]:[port number] |
When you create a service (using DRb.start_service
), you give it a URI. dRuby arranges things so that clients that subsequently specify that URI will be connected to this service. Each active DRbServer
has one unique URI.
DRb.start_service(uri, front) |
To connect a client to a service, pass that service’s URI to DRbObject.new_with_uri
.
there = DRbObject.new_with_uri(uri) |
An object that’s associated with the URI is called the front object because it acts as an entrance to the service (see Figure 2, The front object is the gateway to the application). All the method calls that are created by
DRbObject.new_with_uri()
go to this front object. When you write an actual application, you don’t directly associate the model object of the application; rather, you have a proxy object that handles access control or batches multiple operations.