![]() |
Articles Feed |
Categories
Archives
- July 2010 (5)
- June 2010 (4)
- April 2010 (3)
- March 2010 (2)
- February 2010 (2)
- January 2010 (1)
- December 2009 (1)
- October 2009 (2)
- September 2009 (2)
- August 2009 (1)
- July 2009 (5)
- June 2009 (2)
- May 2009 (2)
- April 2009 (8)
- March 2009 (7)
- January 2009 (2)
- December 2008 (3)
- November 2008 (5)
- October 2008 (4)
- September 2008 (6)
- August 2008 (4)
- July 2008 (5)
- June 2008 (5)
- May 2008 (4)
- April 2008 (2)
- February 2008 (4)
- January 2008 (2)
- December 2007 (2)
- November 2007 (2)
- October 2007 (2)
- September 2007 (1)
- August 2007 (3)
- July 2007 (1)
- June 2007 (4)
- May 2007 (7)
- April 2007 (2)
- February 2007 (3)
- January 2007 (3)
- November 2006 (3)
- October 2006 (3)
- September 2006 (17)
- November 2004 (1)
Rinda 101
by: jim | February 11th, 2008 |
Background
When building a software system composed of multiple decoupled components, the need typically arises for interprocess coordination and communication. As an example, say that we have a customer requirement that allows emails to be sent based on certain events or conditions that occur while the user is interacting with a web application. We build an email component that takes care of the details of actually sending of the email. The web application could use the email component directly, but that would introduce a dependency that we’d rather avoid. How can the two processes communicate without creating a dependency between the two?
If you’re using Ruby, there’s an option that is built right into the standard library. The Rinda module implements the Linda distributed computing paradigm in Ruby. That’s great, but what is Linda? Linda is a coordination language developed by a couple of Yale researchers in the early 1990s. Linda itself is built on the concept of a tuplespace. A tuple is an ordered list of objects, and a tuplespace contains a set of tuples which can be accessed concurrently. A basic set of operations were defined by the language to allow reads, writes and takes.
Getting back to the example, how can Rinda be utilized to enable communication between the web application and the email component? First, a tuplespace will need to be setup that both components can access. When a condition arises within the web application that requires an email to be sent on its behalf, a specific tuple will be written to the tuplespace. The email component is looking for tuples that match a specific pattern. When it finds the tuple written by the web application that matches this pattern, it processes the request and reports back to the tuplespace when finished. In this way, we end up with only data coupling between the two processes, which is a much looser coupling than a direct dependency. This type of approach bears some resemblance to a blackboard system.
Now that we know a little bit about what Rinda can buy us, let’s dive in and try it out.
Hello World
Let’s put together a simple server and client to demonstrate how each works.
Here’s a simple server (server.rb):
- require "rinda/tuplespace"
- port = 4000
- ts = Rinda::TupleSpace.new
- DRb.start_service("druby://:#{port}", ts)
- puts "Rinda listening on #{DRb.uri}..."
- DRb.thread.join
We start by setting the default port to 4000. We then create a new TupleSpace object. The call to start_service starts a local dRuby server listening on port 4000. Passing the TupleSpace as the second parameter sets the server’s front object to the TupleSpace. The URI of the server is output, and the program waits for a kill signal, and we have a Rinda server ready to be accessed by a client. To start the server:
$ ruby server.rb
Here’s a simple interactive client (client.rb). It allows input to be gathered on standard input, with the form
- require "rinda/rinda"
- include Rinda
- port = 4000
- ts = DRbObject.new(nil, "druby://:#{port}")
- while message = gets
- begin
- args = message.split(" ")
- method = args.shift.to_sym
- topic = args.shift.to_sym
- message = args.shift
- message = nil if message == "nil"
- tuple = [topic, message]
- puts ts.send(method, tuple)
- rescue Exception => e
- puts e.message
- end
- end
We’re using the send method in order to invoke the method specified on standard input. There’s also a conversion between nil as a string into nil proper. This is necessary because of wildcards, which we’ll get to in a minute. With the server running, run the client to start a new interactive session:
$ ruby client.rb
Below is a sample session (output is indented for clarity):
write message hello Rinda::TupleEntry:0x4f998 write message world Rinda::TupleEntry:0x4cfa4 read message nil message hello take message nil message hello take message nil message world
The inputs above translate to these method calls:
ts.write([:message, "hello"]) => Rinda::TupleEntry:0x4f998 ts.write([:message, "world"]) => Rinda::TupleEntry:0x4cfa4 ts.read([:message, nil]) => "hello" ts.take([:message, nil]) => "hello" ts.take([:message, nil]) => "world"
Note that reads do not remove the tuple from the tuplespace. The tuple is left untouched such that other clients can access the same data. This is an important concept when dealing with synchronization that we’ll return to in a future post.
The structure of the patterns used in the read and take methods deserve some additional attention. When the tuplespace is queried through a read or a take, there are two criteria used to determine if a tuple matches. First, the length of the tuple must match the pattern’s length. In the example above, all patterns are pairs (2-tuples), so only tuples that are pairs will qualify. Second, the tuple must match the pattern specified. When specifying a pattern, nil is used as a wildcard, which will match anything.
Assume that our tuplespace has the following tuples:
1) [:message] 2) [:message, "hello"] 3) [:message, "hello", "world"]
If the pattern [nil] is specified, e.g. ts.read([nil]), only #1 will match.
If the pattern [:message, nil] is specified, only #2 will match. Of course, [:message, “hello”] would also result in #2 matching as well, while [:message, “world”] would not.
Using [:message, nil, nil], [:message, “hello”, nil], or [:message, nil, “world”] would all result in #3 matching. So it is important to remember that patterns are matched on both length and content of the tuple.
Conclusion
We haven’t even scratched the surface of what can be done with Rinda. This introduction only gets the simplest client and server talking to each other. Stay tuned for more.

October 18th, 2008 at 06:32 PM hey, thanks for this little page! So much of the documentation on the Tuplespace stuff in Ruby is RingServer oriented...and that's really not useful nor necessary to getting Tuplespaces working for you. Instead, it's a cute distraction of "coolness" that diverts from the goal: Tuplespaces put to work. In fact in a mixed network (i.e. non-trivial home LAN or some server farm), the RingServer is useless. It actually took some searching to find a non-RingServer oriented page that gave the how to. Now I'm back to what I remember from 2002 when things were easy...we'll put Tuplespaces to work now. Thanks!
March 18th, 2009 at 03:51 AM
Thank you for the article. Never heard of Rinda... Got really interested. Are there any other useful applications of Rinda?