Scaling your Application Across Nodes with Spring Python's Remoting

Exclusive offer: get 80% off this eBook here
Spring Python 1.1

Spring Python 1.1 — Save 80%

Create powerful and versatile Spring Python applications using pragmatic libraries and useful abstractions

₨831.00    ₨166.20
by Greg L. Turnquist | May 2010 | Open Source Web Development

Spring Python offers a clean cut way to take simple applications and split them out between multiple machines using remoting techniques that can be seamlessly injected without causing code rewrite headaches. Spring Python makes it easy to utilize existing technologies, while also being prepared to support ones not yet designed.

In this article by Greg Lee Turnquist, author of Spring Python 1.1, we will learn how:

  • Pyro provides a nice Python-to-Python remoting capability to easily create client-server applications
  • Spring Python seamlessly integrates with Python so that your application doesn't have to learn the API
  • You can convert a simple application into a distributed one, all on the same machine
  • It takes little effort to rewire an application by splitting it up into parts, plugging in a round-robin queue manager, and running multiple copies of the server with no impact to our business logic

(For more resources on Spring, see here.)

With the explosion of the Internet into e-commerce in recent years, companies are under pressure to support lots of simultaneous customers. With users wanting richer interfaces that perform more business functions, this constantly leads to a need for more computing power than ever before, regardless of being web-based or thick-client. Seeing the slowdown of growth in total CPU horsepower, people are looking to multi-core CPUs, 64-bit chips, or at adding more servers to their enterprise in order to meet their growing needs.

Developers face the challenge of designing applications in the simple environment of a desktop scaled back for cost savings. Then they must be able to deploy into multi-core, multi-server environments in order to meet their companies business demands.

Different technologies have been developed in order to support this. Different protocols have been drafted to help communicate between nodes. The debate rages on whether talking across the network should be visible in the API or abstracted away. Different technologies to support remotely connecting client process with server processes is under constant development.

Introduction to Pyro (Python Remote Objects)

Pyro is an open source project (pyro.sourceforge.net) that provides an object oriented form of RPC. As stated on the project's site, it resembles Java's Remote Method Invocation (RMI). It is less similar to CORBA (http://www.corba.org), a technology-neutral wire protocol used to link multiple processes together, because it doesn't require an interface definition language, nor is oriented towards linking different languages together. Pyro supports Python-to-Python communications. Thanks to the power of Jython, it is easy to link Java-to-Python, and vice versa.

Python Remote Objects is not to be confused with the Python Robotics open source project (also named Pyro).

Pyro is very easy to use out of the box with existing Python applications. The ability to publish services isn't hard to add to existing applications. Pyro uses its own protocol for RPC communication.

Fundamentally, a Pyro-based application involves launching a Pyro daemon thread and then registering your server component with this thread. From that point on, the thread along with your server code is in stand-by mode, waiting to process client calls. The next step involves creating a Pyro client proxy that is configured to find the daemon thread, and then forward client calls to the server. From a high level perspective, this is very similar to what Java RMI and CORBA offer. However, thanks to the dynamic nature of Python, the configuration steps are much easier, and there are no requirements to extend any classes or implement any interfaces..

As simple as it is to use Pyro, there is still the requirement to write some minimal code to instantiate your objects and then register them. You must also code up the clients, making them aware of Pyro as well. Since the intent of this article is to dive into using Spring Python, we will skip writing a pure Pyro application. Instead, let's see how to use Spring Python's out-of-the-box Pyro-based components, eliminating the need to write any Pyro glue code. This lets us delegate everything to our IoC container so that it can do all the integration steps by itself. This reduces the cost of making our application distributed to zero.

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 a little formatting inside the Python shell.

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

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 "\n".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.

Spring Python 1.1 Create powerful and versatile Spring Python applications using pragmatic libraries and useful abstractions
Published: May 2010
eBook Price: ₨831.00
Book Price: ₨1,386.00
See more
Select your format and quantity:

(For more resources on Spring, see here.)

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 Python 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

To finish the server-side of things, let's write a server script to startup and advertise our service

from springpython.context import *

from simple_service_server_ctx import *

if __name__ == "__main__":
ctx = ApplicationContext(HappyBirthdayContext())
ctx.get_object("service_exporter")

Now let's start up our server process.

As you can see, it is standing by, waiting to be called.

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.

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 "\n".join(s.happy_birthday("Greg"))

Now let's run our client.

Unfortunately, nothing is printed out 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.

We restart it and call the client script again.

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 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 to 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.

Scaling our application

At the beginning of this article, we talked about the need to scale applications. So far, we have shown how to take a very simple client-server application and split it between two instances of Python.

However, scaling applications typically implies running multiple copies of an application in order to handle larger loads. Usually, some type of load balancer is needed. Let's explore how we can use Spring Python's remoting services mixed with some simple Python to create a multi-node version of our service.

Converting the single-node backend into multiple instances

The first step is to create another application context. We are going to spin up a different configuration of components, that is a different blue print, and plan to re-use our existing business code without making any changes.

1. Create a two-node configuration, which created two instances of Service, advertised on different ports.

from springpython.config import *
from springpython.context import scope
from springpython.remoting.pyro import *

from simple_service import *

class HappyBirthdayContext(PythonConfig):
def __init__(self):
PythonConfig.__init__(self)

@Object(scope.PROTOTYPE)
def target_service(self):
return Service()

@Object()
def service_exporter1(self):
exporter = PyroServiceExporter()
exporter.service_name = "service"
exporter.service = self.target_service()
exporter.service_port = 7001
return exporter

@Object()
def service_exporter2(self):
exporter = PyroServiceExporter()
exporter.service_name = "service"
exporter.service = self.target_service()
exporter.service_port = 7002
return exporter

2. In this context, we have two different PyroServiceExporters, each with a different port number. While it appears they are both using the target_service, notice how we have changed the scope to PROTOTYPE. This means each exporter gets its own instance. To run this new context, we need a different startup script.

import logging
from springpython.context import *

from multi_server_ctx import *

if __name__ == "__main__":
logger = logging.getLogger("springpython.remoting")
loggingLevel = logging.DEBUG
logger.setLevel(loggingLevel)
ch = logging.StreamHandler()
ch.setLevel(loggingLevel)
formatter = logging.Formatter("%(asctime)s - %(name)s -
%(levelname)s - %(message)s")
ch.setFormatter(formatter)
logger.addHandler(ch)

ctx = ApplicationContext(HappyBirthdayContext())

3. Now let's launch the script:

In this script, we turned on some of Spring Python's built in logging, so we could see some of parts involved with starting up remoting. We can see two different Pyro daemon threads being launched, one for each of the ports. And then we see two instances of our Service code being registered, one with each thread.

We now have two copies of our service ready and waiting to be called.

Creating a round-robin dispatcher

There are many different ways to code a dispatcher. For our example, let's pick a simple round robin algorithm where we cycle through a fixed list

Let's create an application context that includes a round robin dispatcher that acts like our service, fed with a list of PyroProxyFactory elements.

from springpython.config import *
from springpython.remoting.pyro import *

class RoundRobinDispatcher(object):
def __init__(self, proxies):
self.proxies = proxies
self.counter = 0

def happy_birthday(self, parm):
self.counter += 1
proxy = self.proxies[self.counter % 2]
print "Calling %s" % proxy
return proxy.happy_birthday(parm)


class HappyBirthdayContext(PythonConfig):
def __init__(self):
PythonConfig.__init__(self)

@Object
def service_proxies(self):
proxies = []
for port in ["7001", "7002"]:
proxy = PyroProxyFactory()
proxy.service_url ="PYROLOC://127.0.0.1:%s/service" % port
proxies.append(proxy)
return proxies

@Object
def service(self):
return RoundRobinDispatcher(self.service_proxies())

We configured service_proxies as an array of PyroProxyFactorys. This lets us define a static list of connections.

The RoundRobinDispatcher class is injected with the proxies, and mimics the API of our Happy Birthday service. Every call into the dispatcher increments the counter. It then picks one of the proxies to make the actual call.

Adjusting client configuration without client code knowing its talking to multiple node backend

Let's write the client script to use our round robin dispatcher.

from springpython.context import *

from multi_client_ctx import *

if __name__ == "__main__":
ctx = ApplicationContext(HappyBirthdayContext())
s = ctx.get_object("service")

print "\n".join(s.happy_birthday("Greg"))
print "\n".join(s.happy_birthday("Greg"))
print "\n".join(s.happy_birthday("Greg"))

There is hardly an impact to the client. The only difference is the import statement, and the fact that we are calling the service multiple times. If the client was actually a GUI application with this tied to a button, there would be no difference.

Now let's run the client script.

As can be seen, each invocation shows which ProyProxyFactory was called, and they clearly show switching back and forth between the two proxies registered. With each call smoothly integrated with a corresponding PyroServiceExporter, we have scaled our application into a two-node version.

Summary

This example again demonstrates how utilizing an IoC container allows sophisticated wiring of components while not requiring either the client or the server to know about it.

The latest example has totally rewired the original concept of the application simply by coding a different set of blue prints, i.e. IoC configuration. It also demonstrates how things are more powerful and sophisticated when using the pure Python IoC configuration. We were able to mix in code as well as parameters, which is much smoother than writing XML files.

Our application started off as a simple client calling a service. By rewiring it to use IoC, it allowed us to easily reconfigure things to run multiple copies of our service on multiple nodes, without having to change the core logic.

Our round robin dispatcher is admittedly very simplistic. A real production solution would require:

  • More sophisticated error handling to deal with things like remote access exceptions.
  • We would also need a way to grow new nodes and have them added to the list.
  • Perhaps by letting the dispatcher export itself, other services can latch on and find out what services are available.

Spring Python doesn't provide dispatchers, routers, and other types of components used to integrate systems together. Spring Integration (http://www.springsource.org/spring-integration) is a separate part of the Spring portfolio that brings these features to Java. Spring Python Remoting is a building block that in the future can be used to build a Spring Python Integration module.

We could spend countless hours refining our dispatcher. We can also focus on adding more features to our core business logic.

The important factor in all this as that we have demonstrated how Spring Python provides a non-invasive way to scale our original application far beyond what it was originally designed for.

In this article we have learned:

  • Pyro provides a nice Python-to-Python remoting capability to easily create client-server applications.
  • Spring Python seamlessly integrates with Python so that your application doesn't have to learn the API.
  • It is easy to convert a simple application into a distributed one, all on the same machine.
  • We rewired our application by splitting it up into parts, plugging in a round-robin queue manager, and running multiple copies of the server with no impact to our business logic.

Further resources on this subject:


Spring Python 1.1 Create powerful and versatile Spring Python applications using pragmatic libraries and useful abstractions
Published: May 2010
eBook Price: ₨831.00
Book Price: ₨1,386.00
See more
Select your format and quantity:

About the Author :


Greg L. Turnquist

Greg has worked since 1997 as a software engineer at Harris Corporation, always seeking the right tool for the job. Since 2002, Greg has been part of the senior software team working on Harris' $3.5 billion FAA telco program, architecting mission-critical enterprise apps while managing a software team. He provides after hours support and 2nd-level engineering to support the nation-wide telco network and is no stranger to midnight failures and software triage. In 2010, Greg joined the SpringSource division of VMware.

Being a test-bitten script junky, Greg has used JUnit, TestNG, JMock, FEST, PyUnit, and pMock testing frameworks, along with other agile practices to produce top-quality code.

He has worked with Java, Spring, Spring Security, AspectJ, and Jython technologies and also developed sophisticated scripts for *nix and Windows platforms. Being a wiki evangelist, he also deployed a LAMP-based wiki website to provide finger-tip knowledge to users.

In 2006, Greg created the Spring Python project. The Spring Framework provided many useful features, and he wanted those same features available when working with Python.

Greg completed a master's degree in Computer Engineering at Auburn University, and lives in the United States with his family.

Books From Packt


Spring Security 3
Spring Security 3

Python 3 Object Oriented Programming
Python 3 Object Oriented Programming

Python Testing: Beginner's Guide
Python Testing: Beginner's Guide

ASP.NET Data Presentation Controls Essentials
ASP.NET Data Presentation Controls Essentials

Spring Persistence with Hibernate
Spring Persistence with Hibernate

Spring Web Flow 2 Web Development
Spring Web Flow 2 Web Development

Expert Python Programming
Expert Python Programming

Plone 3 Site Administration
Plone 3 Site Administration


Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software