2012/2/18 Alexey Kachayev <kach...@gmail.com>:
> Hi everybody!
>
> Actually, I use Tornado + PyZMQ stack in all my projects. I use different
> approach to separate jobs from main process: I have special abstraction Job
> and I can explicitly "say" to my application whether I want to push this
> request to zmq socket or just handle here. It's convenient for example for
> invalid requests and those situations when I need request's fanout. What
> benefits can we get from your approach? External HTTP balancer will do the
> same thing, am I right (we just should run separated Tornado instances on
> different ports)?
The advantage of new approach is that you can start out and develop
your tornado based web app as you normally would (without zeromq).
You just start writing RequestHandler classes and putting them in a
single tornado web.Application. As you discover that some of the
RequestHandlers are becoming bottlenecks, you can simply run them in
other processes in a load balanced manner. Do accomplish this, the
changes needed to the code are almost trivial - no changes to your
RequestHandlers are needed.
But, there are definitely times where you want to scale a web
application by using ZeroMQ sockets in the manner you are describing.
We actually have another pull request under review that adds some
helper classes to PyZMQ to cover these usage cases:
https://github.com/zeromq/pyzmq/pull/174
This is basically a simple ZeroMQ based RPC setup that again uses
ROUTER/DEALER sockets for load balancing. If you need this type of
thing we would love your comments on that PR as well. We use both of
these approaches in building tornado based web apps.
> I didn't read full code carefully, but I see one inconvenience: how can I
> set timeout per request? In running applications it's very often situation,
> that after some time of waiting backend response you want to do other action
> (e.g. finish request with "Internal timeout error"). As far as I understood
> code, it can be done here:
>
> https://github.com/zeromq/pyzmq/commit/f261a5f9f8dcd75ebe6e9f1f1885bdf00fc05214#L1R102
Great point! I will look into this.
> We just should to check whether Timeout or TimeoutCallback is set (or both)
> and create DelayCallback to call it if request is not finished yet.
>
> There also some questions to code. E.g. do we really need local variables
> here:
>
> https://github.com/zeromq/pyzmq/commit/f261a5f9f8dcd75ebe6e9f1f1885bdf00fc05214#L1R255
Probably not, I will clean that up.
--
Brian E. Granger
Cal Poly State University, San Luis Obispo
bgra...@calpoly.edu and elli...@gmail.com
-Ben
Thanks for looking at this.
On Sun, Feb 19, 2012 at 2:02 AM, Ben Darnell <b...@bendarnell.com> wrote:
> I don't condone the monkey-patching at the end of this file (i.e.
> assignment to attributes of tornado.web). And there's an unfortunate
> amount of duplicated code that seems likely to break over time. Let's
> look at this from a different direction: What changes would be needed
> on the tornado side to implement this cleanly?
I agree the monkey patching is not great. There are two ways we could
address this. The reason the monkey patching is there is that there
are methods inside tornado.web that use some of the existing request
handler classes (like RedirectHandler). I am currently not overriding
those methods, but I need them to use the ZMQ versions of those
request handlers. Two approaches:
* I could just override those methods, leaving the logic the same but
using the ZMQ versions of the request handlers. This requires no
changes to tornado, but will mean there is more code duplication in
our subclasses.
* Make those request handlers class attributes that our subclasses can
set. This would require a minimal change to tornado, but would
require future changes to tornado to continue to use this usage
pattern. I could submit a PR for this.
I a mine with either of these, which would you prefer.
> I'm thinking that the
> cleanest solution may be for the worker to use unmodified Application
> and RequestHandler classes, but with a subclass of HTTPServer that
> uses zmq instead of real sockets. Things look better on the proxy
> side, although it's not clear why you're checking xsrf cookies on the
> proxy side rather than just passing it through. (also, could you do
> this in prepare() instead of _execute()? See FallbackHandler for
> example).
I will have a look at the xsrf stuff, I may be able to clean it up.
Cheers,
Brian
--
-Ben
OK I have created a tornado PR that implements the
redirect_handler_class and error_handler_class settings:
https://github.com/facebook/tornado/pull/467
I will also remove the monkey patching code from PyZMQ.
Thanks!
Brian
I have figured out a way to implement all of this without subclassing
RequestHandler on the backend side of things. This means the tornado
PR I put up earlier today is no longer needed. The code is much
cleaner now and also supports flush.
Cheers,
Brian
* The backend can use regular web.RequestHandlers - no custom
ZMQRequestHandler is needed. This means your existing handler classes
will just work.
* Different choices for how the frontend/backend communication. This
includes a new streaming mode. See the examples.
* More testing and improved examples.
* Our subclasses do much less. We are trying to use the logic of the
base tornado classes as much as possible.
* Timeouts are implemented.
Cheers,
Brian
def process_msg(message):msg = json.loads(message[0],object_hook=json_util.object_hook)if msg["type"] == "ping":# ping and broadcast go to every one (broadcast not implemented yet)for chat in active_chats:for connection in active_chats[chat].values():#logging.error(connection)connection["callback"](msg)else:if msg["chat"] in active_chats:for chat in active_chats[msg["chat"]]:active_chats[msg["chat"]][chat]["callback"](msg)if __name__ == "__main__":ctx = zmq.Context()pusher = ctx.socket(zmq.PUSH)pusher_stream = zmqstream.ZMQStream(pusher, my_ioloop)receiver = ctx.socket(zmq.PULL)receiver_stream = zmqstream.ZMQStream(receiver, my_ioloop)receiver_stream.on_recv(process_msg)my_ioloop.start()