[Twisted-Python] Guidance on Proxy-type Application
glyph at divmod.com
glyph at divmod.com
Thu May 28 16:58:00 MDT 2009
On 01:23 am, asb.bush at gmail.com wrote:
>I have just started to look at the Twisted framework and would like to
>put it
>to use for a new project I am working on. Not being very familiar with
>the
>framework and fairly new to Python in general I would like to ask a
>design/architecture question. (I have written similar applications in
>C but
>would prefer to start this in the right direction and not write Python
>like
>C.)
Thanks for asking!
I apologize for the delay in my answer. I started writing up a simple
example (attached) but was discouraged to find that it was 100 lines
long and required too much explaining.
Then I started documenting it and explaining every line but that was a
very long, tedious message. So, it doesn't have much in the way of
explanation; I hope you will find it useful regardless.
>The application has the following model:
>Many clients connect to the Application and prefer to leave the
>connection
>open. They will send messages across this connection. They will
>expect to
>get a message back at some point later, they do not wait for a response
>(async). The clients are already coded (legacy) and just need to send
>their
>proprietary protocol to the new Application (written using Twisted).
This is *almost* a FAQ. At least, you may find this to be a useful
answer:
<http://twistedmatrix.com/trac/wiki/FrequentlyAskedQuestions#HowdoImakeinputononeconnectionresultinoutputonanother>
>The Twisted application will take the data from the clients and do some
>transformation on it then send the message on to another server (3rd
>party).
>This connection to "another" server must be a single connection, not
>one
>connection per client. This connection should also be persistent and
>not
>opened/closed for each client message sent. Ideally if the 3rd party
>server
>is down then I would also not accept client connections as the messages
>are
>time sensitive and should not be stored and forwarded. At some point
>the 3rd
>part will send a message back and the Application will route it back to
>the
>original source. Basically request/reply pattern.
The example that I've attached does basically this. Run it and then run
'telnet localhost 4322', and type some lines; you will see that they are
transformed and echoed back to you, both by the proxy and by the
protocol being proxied.
At a high level, the answer to your question is so simple that it's hard
to express. Basically, you just need to have all the relevant objects
having references to each other, and calling methods to achieve the
desired effect. The less magic, the better.
More precisely, you need an object responsible for managing your
outgoing connections to your legacy server, so that it can handle
disconnection and reconnection, queueing messages and so on. Then you
need your proxy server factory to hold a reference to that object, so
that it can create references from each proxy server protocol connection
object to the connection manager.
This is related to another recent thread - you can see my message in
that thread here:
http://thread.gmane.org/gmane.comp.python.twisted/18377/focus=18385
>I have been reading through the archives and the twisted docs and have
>also
>looked over the Hex-dump port-forwarding recipe but not found anything
>that
>explains how to use twisted for this model. Hex-dump is close but
>opens/closes the connection to the server on each client connection.
I'm not sure why hex-dump port-forwarding is particularly relevant to
this example. Is it just because this is an application that connects
from one host to another?
>I am thinking that there will be two Factories [and two protocols: 1)
>for
>clients and 2) for 3rd party]. I am not sure how to best establish
>both the
>listening factory and the client to 3rd party factory. Once they are
>established what is the preferred way in Twisted to pass a message from
>one
>protocol to another?
This part of your question is almost exactly the FAQ I mentioned above
:). To reiterate that answer, you just need to have references between
objects, and call methods on the objects you want to do stuff.
If you have a client connection object, just get a reference to that
from the relevant server connection object and call methods on the
client object to emit messages on the client protocol, handling any
responses appropriately. Deferreds can help with that latter part.
It is always better if you can establish that reference as simply as
possible; for example, by passing parameters to the __init__ of various
classes. Again, for reasons that have nothing to do with Twisted
specifically, it's a bad idea to try to establish these references by
having global variables floating around.
Here's a very very simple example of the "good way" to propagate some
data to protocol instances that need it:
class MyProtocol(Protocol):
def __init__(self, data):
self.data = data
class MyFactory(Factory):
def __init__(self, data):
self.data = data
def buildProtocol(self, addr):
return MyProtocol(self.data)
reactor.listenTCP(8765, MyFactory("some data"))
and here's a simple example of a really bad way (don't do this!):
class MyProtocol(Protocol):
def connectionMade(self):
self.bleh = bleh
bleh = "some data"
f = Factory()
f.protocol = MyProtocol
reactor.listenTCP(9876, f)
Even in C, I'm pretty sure it's better style to pass structures to
functions than to abuse piles of local variables :). I only illustrate
this bad style here because it seems to be a common antipattern. The
Protocol class itself doesn't take any parameters to __init__, and
Twisted's users don't always realize that protocols and factories and so
on are just regular objects, with no special rules; they just get
methods called on them by the reactor.
>Any pointers or sample code that you can offer is greatly appreciated.
>I
>would really like to cook this in Twisted and not go back to the C way.
Based on what you've said so far, I think you're basically on the right
track. Good luck!
-------------- next part --------------
A non-text attachment was scrubbed...
Name: multiclient.py
Type: application/x-python
Size: 2998 bytes
Desc: not available
URL: </pipermail/twisted-python/attachments/20090528/f3e6c1bf/attachment-0002.bin>
More information about the Twisted-Python
mailing list