[Twisted-Python] XML-RPC, LineReceiver and defer
Jp Calderone
exarkun at divmod.com
Wed Nov 24 07:28:07 MST 2004
On Wed, 24 Nov 2004 09:27:55 +0100, Roland Hedberg <roland.hedberg at adm.umu.se> wrote:
>Hi!
>
> I'm having problems with defer again :-(
> There is something about defer's that I just don't get, obviously.
>
> Anyway, I'd like to get tips on how to solve this problem:
>
> I have a module who on one side listens on XML-RPC and on the other
> uses the LineReceiver protocol to find some information it needs, to
> service the XML-RFC function.
>
> If I have understood it correctly xmlrpc.XMLRFC can return a defer and
> have the XMLRPC layer hold the reply until some event has happend.
Yep.
>
> Now, the LineReceiver protocol has one function for sending a lines and
> another function for receiving them.
>
> So how do I make XMLRPC wait for the right line.
>
> The XML-RPC server should of course be able to handle serveral requests
> at the same time, so there might be more than one outstanding reply on
> the lineReceiver side.
>
> Knowing which reply that is connected to which sent line is doable by
> keeping a queue and using IDs ( the protocol on the LineReceiver side,
> which is no standard protocol, allows for having tags on queries and
> replies).
>
> But how do I keep the connection between the XML-RPC request and the
> line received ?
>
> I though about having defer checking for the change of a value in a
> dictonary, but I don't know if that is doable, is it ?
Hopefully you already have something like this:
from twisted.protocols import basic
class SimpleRequestProtocol(basic.LineReciever):
def connectionMade(self):
self.outstanding = {}
self.counter = 0
def request(self, query):
d = self.outstanding[self.counter] = defer.Deferred()
self.sendLine('%d %s' % (self.counter, query))
self.counter += 1
return d
def lineReceived(self, response):
tag, rest = response.split(None, 1)
self.outstanding.pop(int(tag)).callback(rest)
Letting the XML-RPC side just call `request' and get back a Deferred that fires whenever the response arrives.
>
> And finally I don't want to use callLater if I can avoid it. I'd like
> the performance to be as high as possible. That is, the XMLRPC reply
> should be sent as soon as the LineReceiver has gotten the reply.
> I also don't want to setup and tear down a connection on the
> LineReceiver side for every request that comes in on the XMLRPC side.
> There is one server on the LineReceiver side that gets all the
> questions, so I liked to be able to have one connection going.
>
So, first, you want to be able to get a connected instance:
from twisted.internet import reactor, protocol
def getConnectedProto(host, port):
f = protocol.ClientCreator(reactor, SimpleRequestProtocol)
return f.connectTCP(host, port)
This returns a Deferred that fires with the instance once the connection is made. You can call it from your XML-RPC code, add a callback to the result, and in the callback invoke .request() with whatever args is appropriate.
But you also don't want a connection per request. So you want to cache the connection:
from twisted.internet import defer
class ConnCache:
def __init__(self):
self._cache = {}
def getConnectedProto(self, host, port)
try:
return defer.succeed(self._cache[host, port])
except KeyError:
d = self._makeConn(host, port)
d.addCallback(self._cacheConn, host, port)
return d
def _makeConn(self, host, port):
f = protocol.ClientCreator(reactor, SimpleRequestProtocol)
return f.connectTCP(host, port)
def _cacheConn(self, protoInstance, host, port):
self._cache[host, port] = protoInstance
return protoInstance
So from your XML-RPC server, you can instantiate ConnCache and store the instance. When you need to do something with your SimpleRequestProtocol, you can just call .getConnectedProto() on it. If there's a connection, it'll be quick; if there's not, it'll go make one and save it for next time, too.
Hope this helps,
Jp
More information about the Twisted-Python
mailing list