- Installation
- Blastoff: Hello, World!
- Building a Distributed Logger with Distribunaut
- Avoiding Confusion of Services
- Borrowing a Service with Distribunaut
- Conclusion
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.