[Twisted-Python] Deferred Network requests

Phil Mayers p.mayers at imperial.ac.uk
Mon Mar 31 07:31:27 EDT 2008


Simon Pickles wrote:
> Hi,
> 
> I've been using twisted in a low-level way for some time and am now 
> interested in the higher level aspects.
> 
> Particularly I hope to be able to make deferred requests across a 
> network. I'd like to have a hub server, with many small apps (modules) 
> which are all clients of the hub server.
> 
> I'd like the modules to be able to request information from each other, 
> via the hub, in a deferred way, so the callback will be triggered when 
> the requested information arrives.
> 
> So I need a chain like this:
> 
> ModuleA.RequestName(id).addCallback(modACallback) -> Hub
> Hub.RequestName(id).addCallback(hubCallback)-> ModuleB
> ModuleB.SendNameToHub() -> Hub
> Hub.hubCallback triggered: SendNameToModA() -> ModuleA
> ModuleA.modACallback triggered - request is complete.
> 
> Perhaps one issue is that I have a client->server->client sequence.
> 
> Although it looks slow, I am designing my system to be as concurrent as 
> possible. Also, the hub can make decisions about sharing requests with 
> several modules on a round-robin, or sending requests (and events, 
> particularly) to more than one module.
> 
> I know I can do this through TCP or similar, but hoped someone might 
> suggest which elements of twisted (a very large framework) I should look 
> at more closely.

Perspective broker (pb) would work, provided the values you are passing 
round are plain python types e.g. int, str, list, dict, tuple.

You'd have something like:

from twisted.spread import pb
from twisted.internet import reactor

class Module(pb.Referenceable):
   def __init__(self, name, server, port=1234):
     self.f = pb.PBClientFactory()
     reactor.connectTCP(server, port, f)
     self.f.getRootObject().addCallback(self.signon, name)

   def signon(self, root, name):
     self.root = root
     self.root.callRemote('signon', name, self)

   # methods for use by remote
   def remote_doThing(self):
     return aDeferredThing()

   # methods for use by local process
   def requestName(self, id):
     return self.root.addCallback(requestName2, id)
   def requestName2(self, root, id):
     return root.callRemote('requestName', id)

ModuleA = Module('ModuleA', server)
reactor.run()

...and on the server site:

from twisted.spread import pb

class Server(pb.Root):
   modules = {}

   # function a module registers with
   def remote_signon(self, name, module):
     if name in self.modules:
         self.modules[name].append(module)
     else:
         self.modules[name] = [module]

   # proxy to a copy of ModuleA, with load-balancing
   def remote_modulea_doThing(self, arg):
     modulea_list = self.modules['ModuleA']
     mod = random.choice(modulea_list)
     return mod.callRemote('doThing', arg)

   # proxy wherever...
   def remote_requestName(self, id):
     mod = ...
     return mod.requestName(id)

The above is very hacky - the class-global dict() is wrong, and there is 
no handling of PB disconnection errors and so forth, but the basic idea 
is sound.

> 
> Many thanks!
> 
> Simon
> 





More information about the Twisted-Python mailing list