added Getting Started section to README.twisted
This commit is contained in:
155
README.twisted
155
README.twisted
@@ -6,13 +6,99 @@ Twisted provides solid foundation for asynchronous programming in Python.
|
|||||||
Eventlet makes asynchronous programming look like synchronous, thus
|
Eventlet makes asynchronous programming look like synchronous, thus
|
||||||
achieving higher signal-to-noise ratio than traditional twisted programs have.
|
achieving higher signal-to-noise ratio than traditional twisted programs have.
|
||||||
|
|
||||||
Eventlet working on top of twisted, provides:
|
Eventlet on top of twisted provides:
|
||||||
* [stable twisted]
|
* [stable twisted]
|
||||||
* [usable and readable synchronous style]
|
* [usable and readable synchronous style]
|
||||||
* existing twisted code can be used without any changes
|
* existing twisted code can be used without any changes
|
||||||
* existing blocking code can be used after trivial changes applied
|
* existing blocking code can be used after trivial changes applied
|
||||||
|
|
||||||
|
Eventlet features:
|
||||||
|
|
||||||
|
* utilities for spawning and controlling greenlet execution:
|
||||||
|
api.spawn, api.kill, coros.Job
|
||||||
|
* utilities for communicating between greenlets:
|
||||||
|
coros.event, coros.queue
|
||||||
|
* standard Python modules that won't block the reactor:
|
||||||
|
eventlet.green package
|
||||||
|
* utilities specific to twisted hub:
|
||||||
|
eventlet.twistedutil package
|
||||||
|
|
||||||
|
|
||||||
|
Getting started with eventlet on twisted
|
||||||
|
----------------------------------------
|
||||||
|
|
||||||
|
This section will only mention stuff that may be useful but it
|
||||||
|
won't explain in details how to use it. For that, refer to the
|
||||||
|
docstrings of the modules and the examples.
|
||||||
|
|
||||||
|
There are 2 ways of using twisted with eventlet, one that is
|
||||||
|
familiar to twisted developers and another that is familiar
|
||||||
|
to eventlet developers:
|
||||||
|
|
||||||
|
1. explicitly start the main loop in the main greenlet;
|
||||||
|
2. implicitly start the main loop in a dedicated greenlet.
|
||||||
|
|
||||||
|
To enable (1), add this line at the top of your program:
|
||||||
|
from eventlet.twistedutil import join_reactor
|
||||||
|
then start the reactor as you would do in a regular twisted application.
|
||||||
|
|
||||||
|
For (2) just make sure that you have reactor installed before using
|
||||||
|
any of eventlet functions. Otherwise a on-twisted hub select will be
|
||||||
|
selected and twisted code won't work.
|
||||||
|
|
||||||
|
Most of examples/twisted_* use twisted style with the exception of
|
||||||
|
twisted_client.py and twisted_srvconnector.py. All of the non-twisted
|
||||||
|
examples in examples directory use eventlet-style (they work with any
|
||||||
|
of eventlet's hubs, not just twisted-based).
|
||||||
|
|
||||||
|
Eventlet implements "blocking" operations by switching to the main loop
|
||||||
|
greenlet, thus it's impossible to call a blocking function when you are
|
||||||
|
already in the main loop. Therefore one must be cautious in a twisted
|
||||||
|
callback, calling only a non-blocking subset of eventlet API here. The
|
||||||
|
following functions won't unschedule the current greenlet and are safe
|
||||||
|
to call from anywhere:
|
||||||
|
|
||||||
|
1. Greenlet creation functions: api.spawn, coros.Job*.spawn_new,
|
||||||
|
twistedutil.deferToGreenThread and others based on api.spawn.
|
||||||
|
|
||||||
|
2. send(), send_exception(), poll(), ready() methods of coros.event,
|
||||||
|
coros.Job and _unbounded_ coros.queue.
|
||||||
|
|
||||||
|
For an example on how to take advantage of eventlet in a twisted
|
||||||
|
application using deferToGreenThread see examples/twisted_http_proxy.py
|
||||||
|
|
||||||
|
Although eventlet provides eventlet.green.socket module that implements
|
||||||
|
interface of the standard Python socket, there's also a way to use twisted's
|
||||||
|
network code in a synchronous fashion via GreenTransport class.
|
||||||
|
A GreenTransport interface is reminiscent of socket although it's not a drop
|
||||||
|
in replacement. It combines features of TCPTransport and Protocol in a single
|
||||||
|
object:
|
||||||
|
|
||||||
|
* all of transport methods (like getPeer()) are available directly on
|
||||||
|
a GreenTransport instance; in addition, underlying transport object
|
||||||
|
is available via 'transport' attribute;
|
||||||
|
* write method is overriden: it may block if transport write buffer is full;
|
||||||
|
* read() and recv() methods are provided to retrieve the data from protocol
|
||||||
|
synchronously.
|
||||||
|
|
||||||
|
To make a GreenTransport instance you can use
|
||||||
|
twistedutil.protocol.GreenTransportCreator (usage is similar to that of
|
||||||
|
twisted.internet.protocol.ClientCreator)
|
||||||
|
|
||||||
|
For an example on how to get a connected GreenTransport instance,
|
||||||
|
see twisted_client.py, twisted_srvconnect.py or twisted_portforward.py.
|
||||||
|
For an example on how to use GreenTransport for incoming connections,
|
||||||
|
see twisted_server.py, twisted_portforward.py.
|
||||||
|
|
||||||
|
|
||||||
|
also
|
||||||
|
* twistedutil.block_on - wait for a deferred to fire
|
||||||
|
block_on(reactor.callInThread(func, args))
|
||||||
|
* twistedutil.protocol.basic.LineOnlyReceiverTransport - a green transport
|
||||||
|
variant built on top of LineOnlyReceiver protocol. Demonstrates how
|
||||||
|
to convert a protocol to a synchronous mode.
|
||||||
|
|
||||||
|
|
||||||
Coroutines
|
Coroutines
|
||||||
----------
|
----------
|
||||||
|
|
||||||
@@ -21,33 +107,45 @@ http://codespeak.net/py/dist/greenlet.html
|
|||||||
|
|
||||||
Essential points
|
Essential points
|
||||||
|
|
||||||
* Every greenlet except MAIN has a parent. When an exception happen in the greenlet
|
* There always exists MAIN greenlet
|
||||||
it is propogated to the parent greenlet (special case is GreenletExit, in that case
|
* Every greenlet except MAIN has a parent. MAIN therefore could be detected as g.parent is None
|
||||||
the greenlet silently dies)
|
* When greenlet is finished it's return value is propagated to the parent (i.e. switch() call
|
||||||
* Parent can be reassigned (cycle would be detected and rejected with ValueError)
|
in the parent greenlet returns it)
|
||||||
...
|
* When an exception leaves a greelen, it's propagated to the parent (i.e. switch() in the parent
|
||||||
|
re-raises it) unless it's a subclass of GreenletExit, which is returned as a value.
|
||||||
|
* parent can be reassigned (by simply setting 'parent' attribute). A cycle would be detected and
|
||||||
|
rejected with ValueError
|
||||||
|
|
||||||
|
|
||||||
greenlet == coroutine == green thread == microthread in this document
|
greenlet == coroutine == green thread == microthread in this document
|
||||||
|
|
||||||
|
Note, that there's no scheduler of any sort; if a coroutine wants to be scheduled again
|
||||||
|
it must take care of it itself. As an application developer, however, you don't need
|
||||||
|
to worry about it as that's what eventlet does behind the scenes.
|
||||||
|
|
||||||
|
|
||||||
How does eventlet work
|
How does eventlet work
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
Twisted's reactor and eventlet's hub are very similar in what they do.
|
Twisted's reactor and eventlet's hub are very similar in what they do.
|
||||||
Both continuously call select (or poll or similar) on the list of registered
|
Both continuously preform polling on the list of registered descriptors
|
||||||
descriptors (this is called polling) and each time a specific event is
|
and each time a specific event is fired, the associated callback function
|
||||||
fired, the associated callback function is called. In addition, both
|
is called. In addition, both maintain a list of scheduled calls.
|
||||||
maintain a list of scheduled calls.
|
|
||||||
|
|
||||||
Polling is performed by the main loop - a function that both reactor and hub have.
|
Polling is performed by the main loop - a function that both reactor and hub have.
|
||||||
However, while with twisted you explicitly start it by calling reactor.run(), with
|
When twisted calls user's callback it's expected to return almost immediately,
|
||||||
eventlet the main loop is running in a dedicated greenlet and is started implicitly
|
without any blocking I/O calls. Deferreds help there.
|
||||||
upon the first switch into that greenlet. When an event is fired, the callback
|
|
||||||
perform switch to the waiting greenlet.
|
|
||||||
|
|
||||||
So, to perform a `blocking' operation that blocks this greenlet but not the others,
|
Eventlet runs the main loop in a dedicated greenlet (MAIN_LOOP). It is the same
|
||||||
you switch() into the main loop's greenlet with some additional preparations,
|
greenlet as MAIN if you use join_reactor. Otherwise it's a dedicated greenlet
|
||||||
as shown in the following figure.
|
started implicitly. The execution is organized in a such way that the switching
|
||||||
|
almost always involves MAIN_LOOP. All of the blocking use this algorithm:
|
||||||
|
|
||||||
|
1. register a callback that switches back to the current greenlet when
|
||||||
|
an event of interest happen
|
||||||
|
2. switch to the MAIN_LOOP
|
||||||
|
|
||||||
|
For example, here's what eventlet's socket recv() does:
|
||||||
|
|
||||||
= blocking operation RECV on socket d =
|
= blocking operation RECV on socket d =
|
||||||
|
|
||||||
@@ -70,24 +168,3 @@ data=MAIN_LOOP.switch() ---------> poll for events
|
|||||||
\--------- USER.switch(data) # argument data here becomes return value in user's switch
|
\--------- USER.switch(data) # argument data here becomes return value in user's switch
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
TBW:
|
|
||||||
|
|
||||||
* green package
|
|
||||||
modules that mimic modules in standard library, but actually do the non-blocking stuff as described above.
|
|
||||||
|
|
||||||
* how to convert protocol implemented in twisted to `blocking' mode.
|
|
||||||
see eventlet.twisteds.util.block_on
|
|
||||||
|
|
||||||
One can also look at Corotwine for an ideas.
|
|
||||||
Corotwine is another attempt to use twisted together with greentlets.
|
|
||||||
protocol <-> buffer <-> greenlet
|
|
||||||
|
|
||||||
* how to convert real blocking code.
|
|
||||||
For simple cases: block_on(reactor.callInThread(func, args))
|
|
||||||
|
|
||||||
When single deferred is not enough: Queue subclass that has list of
|
|
||||||
associated channels with it and a descriptor (obtained with os.pipe()).
|
|
||||||
Every time, queue.put is called, eQueue does descriptor.write(byte).
|
|
||||||
The descriptor is registered with the hub, so the callback is called
|
|
||||||
which gets the value from the Queue and puts it in all associated channels.
|
|
||||||
|
Reference in New Issue
Block a user