Converting a simple application into a distributed one on the same machine

For this example, let's develop a simple service that processes some data and produces a response. Then, we'll convert it to a distributed service.

First, let's create a simple service. For this example, let's create one that returns us an array of strings representing the Happy Birthday song with someone's name embedded in it.

class Service(object):
def happy_birthday(self, name):
results = []
for i in range(4):
if i == 2:
results.append("Happy Birthday Dear %s!" % name)
else:
results.append("Happy Birthday to you!")
return results

Our service isn't too elaborate. Instead of printing the data directly to screen, it collects it together and returns it to the caller. This allows us the caller to print it, test it, store it, or do whatever it wants with the result. In the following screen text, we see a simple client taking the results and printing them with a little formatting inside the Python shell.

Converting a simple application into a distributed one on the same machine

As we can see, we have defined a simple service, and can call it directly. In our case, we are simply joining the list together with a newline character, and printing it to the screen.

Fetching the service from an IoC container

Let's define a simple IoC container that will create an instance of our service.

from springpython.config import *
from simple_service import *
class HappyBirthdayContext(PythonConfig):
def __init__(self):
PythonConfig.__init__(self)
@Object
def service(self):
return Service()

Creating a client to call the service

Now let's write a client script that will create an instance of this IoC container, fetch the service, and use it.

from springpython.context import *
from simple_service_ctx import *
if __name__ == "__main__":
ctx = ApplicationContext(HappyBirthdayContext())
s = ctx.get_object("service")
print "
".join(s.happy_birthday("Greg"))

Running this client script neatly creates an instance of our IoC container, fetches the service, and calls it with the same arguments shown earlier.

Creating a client to call the service

Making our application distributed without changing the client

To make these changes, we are going to split up the application context into two different classes: one with the parts for the server and one with parts for the client.

While there is no change to the API, we do have to slightly modify the original client script, so that it imports our altered context file.

First, let's publish our service using Pyro by making some small changes to the IoC configuration.

from springpython.config import *
from springpython.remoting.pyro import *
from simple_service import *
class HappyBirthdayContext(PythonConfig):
def __init__(self):
PythonConfig.__init__(self)
@Object
def target_service(self):
return Service()
@Object()
def service_exporter(self):
exporter = PyroServiceExporter()
exporter.service_name = "service"
exporter.service = self.target_service()
return exporter

We have renamed the service method to target_service, to indicate it is the target of our export proxy.

We then created a PyroServiceExporter. This is Spring Python's out-of-the-box solution for advertising any Python service using Pyro. It handles all the details of starting up Pyro daemon threads and registering our service.

Pyro requires services to be registered with a distinct name, and this is configured by setting the exporter's service_attribute attribute to "service". We also need to give it a handle on the actual service object with the export's service attribute. We do this by plugging in target_service.

  • By default, Pyro advertises on IP address 127.0.0.1, but this can be overridden by setting the exporter's service_host attribute
  • By default, Pyro advertises on port 7766, but this can be overridden by setting the exporter's service_port attribute
  1. To finish the server side of things, let's write a server script to startup and advertise our service.
from springpython.context import *
simple application, converting into distributed applicationwithout, changing the clientfrom simple_service_server_ctx import *
if __name__ == "__main__":
ctx = ApplicationContext(HappyBirthdayContext())
ctx.get_object("service_exporter")

Now let's start up our server process.

Making our application distributed without changing the client
  1. As you can see, it is standing by, waiting to be called.
  2. Next, we need a client-based application context.
from springpython.config import *
from springpython.remoting.pyro import *
class HappyBirthdayContext(PythonConfig):
def __init__(self):
PythonConfig.__init__(self)
@Object
def service(self):
proxy = PyroProxyFactory()
proxy.service_url="PYROLOC://127.0.0.1:7766/service"
return proxy

We define Pyro client using Spring Python's PyroProxyFactory, an easy to configure Spring Python proxy factory that handles the task of using Pyro's APIs to find our remote service. And by using the IoC container's original name of service service embedded in the URL (PYROLOC://127.0.0.1:7766/service), our client script won't require any changes.

  1. Let's create a client-side script to use this context.
from springpython.context import *
from simple_service_client_ctx import *
if __name__ == "__main__":
ctx = ApplicationContext(HappyBirthdayContext())
s = ctx.get_object("service")
print "
".join(s.happy_birthday("Greg"))
  1. Now let's run our client.
Making our application distributed without changing the client

Unfortunately, nothing is printed on the screen from the server side. That is because we don't have any print statements or logging. However, if we go and alter our original service at simple_service.py, we can introduce a print statement to verify our service is being called on the server side.

Making our application distributed without changing the client

Let's restart it and call the client script again.

Making our application distributed without changing the client

This shows that our server code is being called from the client. And it's nicely decoupled from the machinery of Pyro.

Is our example contrived?

Does this example appear contrived? Sure it does. However, the basic concept of accessing a service from a client is not.

Instead of our Happy Birthday service, this could be the data access layer of an application, an interface into an airline flight reservation system, or access to trouble ticket data offered by an operations center.

Our example still has the same fundamental concepts of input arguments, output results, and client-side processing after the fact.

Spring Python is non-invasive

We took a very simple service, and by serving it up through an IoC container, it was easy to wrap it with a Pyro exporter without our application realizing it. The IoC container, as demonstrated throughout this book, opens the door to many options. Now, we see how it lends itself to exposing our code as a Pyro service. This concept doesn't stop with Pyro. This same pattern can be applied to export services for other remoting mechanisms. By being able to separate the configuration from the actual business logic, we can yet again apply a useful and practical service without having to rewrite the code to work with the service.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.188.137.58