【问题标题】:Making an asynchronous call synchronous in tornado在龙卷风中进行异步调用同步
【发布时间】:2012-11-09 16:49:42
【问题描述】:

我有以下问题。

我在基于 Tornado 的应用服务器上工作。大多数代码可以是同步的,Web 界面并没有真正使用 Tornado 的任何异步功能。

我现在必须连接到一个(异步)遗留后端,我使用tornado.iostream 接口来发送命令。对这些命令的响应与其他定期信息(例如状态更新)一起异步发送。

代码封装在一个通用接口中,该接口也可用于其他后端。

我想要达到的目标如下:

# this is executed on initialization
self.stream.read_until_close(self.close, self.read_from_backend)

# this is called whenever data arrives on the input stream
def read_from_backend(self, data):
     if data in pending:
         # it means we got a response to a request we sent out
         del self.pending[data]
     else:
         # do something else

# this sends a request to the backend
def send_to_backend(self, data):
     self.pending[data] = True
     while data in self.pending:
          # of course this does not work
          time.sleep(1)
     return 

当然这不起作用,因为time.sleep(1) 不允许read_from_backend() 继续运行。

我该如何解决这个问题?我希望 send_to_backend() 仅在收到响应时返回。有没有一种方法可以让我控制read_from_backend 而无需从该方法返回?

请注意,在 web 层使用 @asynchronous 和 @gen.engine 很难做到这一点,因为这需要完全重写我们 web 层中的所有请求。有没有办法可以在其他地方实现相同的设计模式?

【问题讨论】:

    标签: python design-patterns tornado coroutine


    【解决方案1】:

    我认为一个好主意可能是考虑使用 gevent。通过 MonkeyPatching 并使用我编写的简单装饰器,您可以非常轻松地获得以同步方式(阻塞样式)编写的漂亮异步视图。

    您可以重用previous answer of mine 中的大部分代码。

    虽然您可能出于不同的原因不想使用 gevent(没有依赖项):

    承认你用猴子修补了你的全局流程:

    from gevent import monkey; monkey.patch_all()
    

    以上补丁线程,套接字,睡眠......所以他们通过gevent的集线器(集线器是gevent ioloop是龙卷风)。

    一旦修补并在我之前的回答中使用 @gasync 装饰器,您的视图可能只是:

    class MyHandler(tornado.web.RequestHandler):
         @gasync
         def get(self):
             # Parse the input data in some fashion
             data = get_data_from_request()
    
             # This could be anything using python sockets, urllib ...
             backend_response = send_data_to_backend(data)
    
             # Write data to HTTP client
             self.write(backend_response)
    
             # You have to finish the response yourself since it's asynchronous
             self.finish()
    

    我发现 gevent 的简单性和“优雅”远远超过了使用 tornado 的 ioloop 编写异步代码的任何优势。

    在我的例子中,我必须使用以同步方式编写的遗留代码,所以基本上 gevent 让生活更安全,我所要做的就是猴子补丁并编写那个装饰器,我可以使用所有遗留代码而无需任何修改。

    我希望这会有所帮助。

    【讨论】:

    • 我不清楚与我原来的问题有什么关系。我必须承认我对 gevent 一无所知。
    • Gevent 是一个基于协程的 Python 网络库。如果您不知道,您当然应该检查一下 (gevent.org)。
    • 我建议您将 gevent 用于异步网络,而不是使用 tornado 的 iostreams。由于 gevent 使用协作协程,一旦你修改了 python 的套接字,你将能够以常规同步风格编写异步网络代码。主要优点是它降低了编写异步网络代码的复杂性,并且使用起来非常简单(您需要做的就是猴子补丁)。
    • 我刚刚看了文档。这看起来非常像我想要的。如果我能在周末后将它连接到龙卷风,我会试试。
    • 不幸的是,gevent 无法在我的旧系统上编译。它是一个 OpenSUSE 系统,大约在 10.0 范围内,我遇到了 libevent 的问题。我不会再追究这个问题,但我会接受答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-12-24
    • 1970-01-01
    • 2014-11-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-30
    相关资源
    最近更新 更多