Automatically Discovering DRb Services with Rinda

Credit: James Edward Gray II

Problem

You want to distribute Ruby code across your local network without hardcoding the clients with the addresses of the servers.

Solution

Using Rubys standard Rinda library, its easy to provide zero-configuration networking for clients and services. With Rinda, machines can discover DRb services without providing any addresses. All you need is a running RingServer on the local network:

#!/usr/bin/ruby # rinda_server.rb require inda/ring # for RingServer require inda/tuplespace # for TupleSpace DRb.start_service # Create a TupleSpace to hold named services, and start running. Rinda::RingServer.new(Rinda::TupleSpace.new) DRb.thread.join

Discussion

The RingServer provides automatic service detection for DRb servers. Any machine on your local network can find the local RingServer without knowing its address. Once its found the server, a client can look up services and use them, not having to know the addresses of the DRb servers that host them.

To find the Rinda server, a client broadcasts a UDP packet asking for the location of a RingServer. All computers on the local network will get this packet, and if a computer is running a RingServer, it will respond with its address. A server can use the RingServer to register services; a client can use the RingServer to look up services.

A RingServer object keeps a service listing in a shared TupleSpace (see Recipe 16.12). Each service has a corresponding tuple with four members:

By retrieving this TupleSpace remotely, you can look up services as tuples and advertise your own services. Lets advertise an object (a simple TupleSpace) tHRough the RingServer under the name :TupleSpace:

#!/usr/bin/ruby # share_a_tuplespace.rb require rinda/ring # for RingFinger and SimpleRenewer require rinda/tuplespace # for TupleSpace DRb.start_service ring_server = Rinda::RingFinger.primary # Register our TupleSpace service with the RingServer ring_server.write( [:name, :TupleSpace, Rinda::TupleSpace.new, Tuple Space], Rinda::SimpleRenewer.new ) DRb.thread.join

The SimpleRenewer sent in with the namespace listing lets the RingServer periodically check whether the service has expired.

Now we can write clients that find this service by querying the RingServer, without having to know which machine it lives on. All we need to know is the name of the service:

#!/usr/bin/ruby # use_a_tuplespace.rb require inda/ring # for RingFinger require inda/tuplespace # for TupleSpaceProxy DRb.start_service ring_server = Rinda::RingFinger.primary # Ask the RingServer for the advertised TupleSpace. ts_service = ring_server.read([:name, :TupleSpace, nil, nil])[2] tuplespace = Rinda::TupleSpaceProxy.new(ts_service) # Now we can use the object normally: tuplespace.write([:data, rand(100)]) puts "Data is #{tuplespace.read([:data, nil]).last}." # Data is 91.

These two programs locate each other without needing hardcoded IP addresses. Addresses are still being used under the covers, but the address to the Rinda server is discovered automatically through UDP, and all the other addresses are kept in the Rinda server.

Rinda::RingFinger.primary stores the first RingServer to respond to your Ruby processs UDP packet. If your local network is running more than one RingServer, the first one to respond might not be the one with the service you want, so you should probably only run one RingServer on your network. If you do have more than one RingServer, you can iterate over them with Rinda::RingFinger#each.

See Also

Категории