Using Request/Reply in a Distributed Middleware Architecture

Credit: Graham Dumpleton

Problem

You need to allow some distributed services to supply methods, and other distributed services to access and use those methods, in a location-independent way by writing a suitable central exchange (middleware) server.

Solution

The OSE package also supports the request/reply architecture. First, we need a central middleware process to which all others connect:

# The central.py script -- needs the OSE package from http://ose.sourceforge.net

import netsvc
import signal

dispatcher = netsvc.Dispatcher(  )
dispatcher.monitor(signal.SIGINT)

exchange = netsvc.Exchange(netsvc.EXCHANGE_SERVER)
exchange.listen(11111)

dispatcher.run(  )

Next, we need a server that supplies methods through the middleware:

# The server.py script -- needs the OSE package from http://ose.sourceforge.net

import netsvc
import signal

class Service(netsvc.Service):

    def _ _init_ _(self):
        netsvc.Service._ _init_ _(self, "math")
        self.joinGroup("web-services")
        self.exportMethod(self.multiply)

    def multiply(self, x, y):
        return x*y

dispatcher = netsvc.Dispatcher(  )
dispatcher.monitor(signal.SIGINT)

exchange = netsvc.Exchange(netsvc.EXCHANGE_CLIENT)
exchange.connect("localhost",11111,5)

service = Service(  )

dispatcher.run(  )

Then, we need a client that consumes methods through the middleware:

# The client.py script -- needs the OSE package from http://ose.sourceforge.net

import netsvc
import signal
import random

class Client(netsvc.Service):

    def _ _init_ _(self):
        netsvc.Service._ _init_ _(self, "")
        self.startTimer(self.call, 1, "1")

    def call(self,name):
        service = self.serviceEndPoint("math")
        if service != None:
            x = int(random.random(  )*1000)
            id = service.multiply(x, x)
            self.monitorResponse(self.result, id)
        self.startTimer(self.call, 1, "1")

    def result(self,square):
        print square

dispatcher = netsvc.Dispatcher(  )
dispatcher.monitor(signal.SIGINT)

exchange = netsvc.Exchange(netsvc.EXCHANGE_CLIENT)
exchange.connect("localhost", 11111, 5)

client = Client(  )

dispatcher.run(  )

We can also write a gateway exposing an XML-RPC service for the same methods:

# The gateway.py script -- needs the OSE package from http://ose.sourceforge.net

import signal
import netsvc
import netsvc.xmlrpc

dispatcher = netsvc.Dispatcher(  )
dispatcher.monitor(signal.SIGINT)

httpd = netsvc.HttpDaemon(8000)
rpcgw = netsvc.xmlrpc.RpcGateway("web-services")
httpd.attach("/xmlrpc/service", rpcgw)
httpd.start(  )

exchange = netsvc.Exchange(netsvc.EXCHANGE_CLIENT)
exchange.connect("localhost", 11111, 5)

dispatcher.run(  )

Discussion

This recipe is a simple example of setting up a distributed message-oriented request/reply architecture. It shows the creation of a central exchange service that all participating processes connect to. Services assign themselves a name and export the methods that are remotely accessible. Client services can then make calls against the exported methods.

This recipe provides an alternative to systems dependent on XML-RPC and SOAP, which only create connections to other processes when required. In this architecture, the processes are always connected through the central exchange process, avoiding the cost of setting up and ripping down the socket connections for each request. That said, an XML-RPC or SOAP gateway can also be connected to the system to allow similar remote access using the HTTP protocol. Although each service is shown in a separate process, they could just as well be in the same process, as the means of communication is the same. The services shown may also publish data, which other services can subscribe to if required, as shown in Recipe 13.8.

The central.py script is the same as in Recipe 13.8, which highlights how the central middleware in OSE architectures is independent from the contents of the application and can offer both publish/subscribe and request/reply mediation. The server.py script defines a subclass of Service and calls the joinGroup and exportMethod methods, which we already examined in Recipe 13.5. The client.py script uses Service’s startTimer method for periodic invocation of its own call method. This method in turn uses serviceEndPoint to access the specific service named 'math', calls the multiply method on the latter, and calls monitorResponse to get its own method result called back when the server responds with a result.

The gateway.py script shows how OSE lets you use the same infrastructure to expose the same services via the Web, as we already illustrated in Recipe 13.5.

See Also

Recipe 13.5; Recipe 13.8 describes a different feature of the OSE package; the OSE package (http://ose.sourceforge.net).

Get Python Cookbook now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.