Home > Articles > Programming > General Programming/Other Languages

  • Print
  • + Share This
This chapter is from the book

This chapter is from the book

Avoiding Confusion of Services

Earlier, when speaking about application names, I mentioned that names need to be unique to avoid confusion, but I didn't explain what I meant.

You know from Chapter 2, "Rinda," that when we create a Tuple to put into the RingServer, we give it some unique characteristics that allow us to retrieve it easily. The combination of these characteristics becomes sort of like an ID for that particular Tuple. So imagine if we were to put two Tuples into the RingServer that had the same characteristics. How would we retrieve the specific one we want? If we use the same application name, we not only run the risk of overwriting another Tuple, but we also make it difficult to find later.

As you have seen, Distribunaut performs a lot of magic that keeps us from having to write as much code. It also makes the code we write cleaner and easier to use and maintain. One thing Distribunaut does for you is build the search characteristics for you when you make a call to the special Distribunaut::Distributed module. When Distribunaut goes to build the search parameters for that request, it takes into account only the class, or service, name you provide. Because of this, if you have two applications serving up a class with the same name, you are unsure which one you will receive from the query. In some cases this might be fine, but in other cases, it might be a problem.

Let's look at a simple example. Let's create a service that serves up a User class. We want to launch at least two instances of this service for this example. To do that we need to run the following code twice to start two instances of it:

require 'rubygems'
require 'distribunaut'

user_servers = ['0']

services = Distribunaut::Utils::Rinda.available_services

services.each do |service|
  if service.app_name.to_s.match(/^user_server_(\d+)$/)
    user_servers << "#{$1}"
  end
end

user_servers.sort!

app_name = "user_server_#{user_servers.last.succ}"

puts "Launching: #{app_name}"

configatron.distribunaut.app_name = app_name

class User
  include Distribunaut::Distributable

  def app_server_name
    configatron.distribunaut.app_name
  end

end

DRb.thread.join

A large majority of the preceding code simply finds out what the last service, if there is one, was called. Then it names the service that is currently launching so that it has a unique name. Although most of this is straightforward Ruby code, it is worth pointing out the call to the available_services method on the Distribunaut::Utils::Rinda module. The available_services method, as its name implies, returns an Array of the services that are currently registered with Distribunaut. The elements of this Array are Distribunaut::Tuple classes, which are simply a convenience class to make it easier to deal with Tuples that are in the Distribunaut format.

After we have decided on an appropriate application name and registered it, we create a User class, include the Distribunaut::Distributable module and give it an instance method that returns the application name that is running this service.

Now, with a couple of instances of our service running, let's look at the style of client we have been using so far in this chapter:

require 'rubygems'
require 'distribunaut'

user = Distribunaut::Distributed::User.new
puts user.app_server_name

So which instance of the user service do we get when we run this? Well, on my system I see the following printed:

user_server_1

On your system you might see this:

user_server_2

or another variation, depending on how many instances you have running. There is no guarantee which instance you will receive when accessing services this way. Again, this might be acceptable in your environment, or it might not.

So what do you do when this is unacceptable, or you want to get a specific instance of a service? Distribunaut provides you with a method called lookup. This method is found on the Distribunaut::Distributed module. The lookup method takes a URL to find the specific instance you are looking for.

Right about now you should be wondering how you are supposed to know the URL of the service you want to look up. Don't worry. Distribunaut has you covered by making it easy to look up these services. Let's look at a client that wants to find specific instances of the user services we have running:

require 'rubygems'
require 'distribunaut'

user_one_url = "distributed://user_server_1/User"
UserOne = Distribunaut::Distributed.lookup(user_one_url)

user_two_url = "distributed://user_server_2/User"
UserTwo = Distribunaut::Distributed.lookup(user_two_url)

user1 = UserOne.new
puts user1.app_server_name

user2 = UserTwo.new
puts user2.app_server_name

Building the URL for the service we want is quite simple. The format is distributed://<application_name>/<service_name>. Because of this format, it is important that we have unique application names for each Ruby VM so that we can easily seek out the one we are looking for.

With the URLs in hand for the two services we are looking for, we can call the lookup method and find these two services. When we have them, we can create new instances of the User class and print the return value of the app_server_name method. You should see something similar to the following printed:

user_server_1
user_server_2

With the lookup method now in our arsenal, we can code with confidence, knowing that we will always get the specific instance of a service we are looking for. And we can do it without having to deal with IP addresses, ports, and other such nonsense.

  • + Share This
  • 🔖 Save To Your Account