【问题标题】:Is tornado an async webserver?龙卷风是异步网络服务器吗?
【发布时间】:2014-09-01 02:09:37
【问题描述】:

我正在学习编写一个可以处理数千个连接的后端服务器。

我查看了一些示例代码,但发现它仍在用同步逻辑编写。

例如:(取自http://www.tornadoweb.org/en/stable/gen.html

@gen.coroutine
def get(self):
    http_client = AsyncHTTPClient()
    response1, response2 = yield [http_client.fetch(url1), http_client.fetch(url2)]
    print(response1.body, response2.body)

很明显print语句在得到两次fetch的响应之前是不能执行的,否则会因为访问不存在的数据而抛出异常。

因此,它的最后两行之间必须有一个block,但是,block,对于非阻塞、异步、事件驱动来说,不是tornado是高亮的...然后,可以处理数千个连接吗?

【问题讨论】:

    标签: python asynchronous block tornado


    【解决方案1】:

    是的,tornado 是异步的。您展示的示例是coroutine;它实际上是非阻塞的,并在yield 调用中将控制权释放回龙卷风事件循环。只有当两个 http_client.fetch 调用都实际完成时,控制才会返回到 get 函数。

    这两个例子实际上在tornado中是等价的:

    class AsyncHandler(RequestHandler):
        @asynchronous
        def get(self):
            http_client = AsyncHTTPClient()
            http_client.fetch("http://example.com",
                              callback=self.on_fetch)
    
        def on_fetch(self, response):
            do_something_with_response(response)
            self.render("template.html")
    

    还有一个协程版本:

    class GenAsyncHandler(RequestHandler):
        @gen.coroutine
        def get(self):
            http_client = AsyncHTTPClient()
            response = yield http_client.fetch("http://example.com")
            do_something_with_response(response)
            self.render("template.html")
    

    协程允许您编写看起来同步的异步代码,这样更具可读性。当上面的代码遇到yield 时,get 挂起并将http_client.fetch 返回的Future 对象返回给gen.coroutine 装饰器。 gen.coroutine 装饰器具有魔力,它可以安排由 fetch 调用返回的 Future 的结果,一旦准备好,就将其传递回 get

    【讨论】:

    • 我只是在做一个问答式的问题......请看看我的回答。
    【解决方案2】:

    是的!

    在长时间运行的作业和缓慢的 IO 操作之间使用 yieldcoroutine 很可能与两种不同的模式混淆。我刚刚在这方面犯了一个严重错误。

    • 在长时间运行的工作中

      1. 其生成器的next()方法被反复调用,每次做一个工作单元。
      2. 如果有多个coroutine在运行,调度器会一一调用每个next()方法,所以它在作业之间共享CPU时间。所以工作之间是合作的,所以叫coroutine
    • 在缓慢的 IO 操作中

      1. next() 方法对每个 yield 点仅调用一次。
      2. 一旦yield 从进行 IO 操作开始,IO 操作已委托给 OS 内核。调度器会在 IO 操作完成时添加一个回调,调用 next() 方法。
      3. 现在调度器的问题是何时调用next() 方法。这由操作系统级别的异步功能提供支持,例如 epollIOCP 在 IO 完成时通知调度程序。
      4. 所以整个流程是,运行到点制作 IO,而不是yield 交出执行。 IO 完成后,它会通过调度程序调用next() 继续执行。
      5. 此控制流的效果与回调模式完全相同,都是
        • 跑到一个点dong IO
        • 移交执行,以便进程可以执行其他工作
        • IO 已完成,继续运行。
        • 唯一的区别是一个继续执行前一个函数,另一个继续执行一个新的回调函数。

    所以,总结一下:

    • 在长时间运行的作业中,调度程序将调用每个next() 方法一旦进程空闲

    • 在慢速 IO 操作中,当 IO 操作完成时,next() 方法只调用一次

    我想如果你意识到这一点,你就会明白使用yieldcoroutine 实际上可以与callback 具有相同的功能。

    【讨论】:

    • 来自 Tornado 文档介绍:“Tornado 是一个 Python Web 框架和异步网络库”。虽然异步和非阻塞之间存在差异,但您在这里有点偏离。
    • Tornado 既是异步的又是非阻塞的。您链接到的文档页面描述了使用回调和协程作为异步的龙卷风代码示例。此外,关于轻量级线程并没有真正实现异步的文章是对gevent 的引用,而不是协程。
    • tornado 文档中描述的“阻塞”和“异步”之间的区别只是“此代码在实际完成工作之前返回吗?”如果是这样,它是异步的。如果它在返回之前等待完成它的工作,它就是阻塞的。 Tornado 允许您以这两种风格编写代码。现在,tornado 还允许您通过协程在同步函数中进行非阻塞 I/O 调用,但这些函数可以很容易地以异步方式使用(通过不让步)。
    • 确实存在非阻塞同步函数,但这意味着与您在这里所说的不同。非阻塞模式下 POSIX 套接字上的 send() 和 recv() 是同步的,因为如果它们返回 EAGAIN,它们就不会在后台继续工作(不像 Windows IOCP,其中套接字调用是真正异步的)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-24
    相关资源
    最近更新 更多