Tornado入门(二)【异步和阻塞IO】

实时Web应用通常针对每个用户创建持久连接,对于传统的同步服务器,这意味着需要给每个用户单独创建一个线程,这样做的代价非常高。

为了减少并发连接的消耗,Tornado采用了单线程事件循环模型,这也就意味着所有的应用代码都必须是异步非阻塞的,因为一次只能有一个活跃的操作。

异步和非阻塞其实紧密关联,通常它们可以互换,但是它们并不是同一个概念。

阻塞

当函数需要等待某件事情的发生并返回结果时,它就处于阻塞状态。一个函数可能因为很多原因阻塞,网络IO,磁盘IO, 互锁等等。实际上,每个函数都会阻塞,当它运行并占用CPU的时候, 都会占用那么一点时间。

函数有些情况可能会阻塞,有些情况又不会阻塞。例如,tornado.httpclient在采用默认配置的情况下,解析DNS的时候会阻塞,但其它网络访问并不会阻塞。在Tornado中,我们谈到的阻塞一般是针对网络IO,而忽略其它的阻塞。

异步

异步函数在结束之前就返回了,它通常在后台触发一些任务,等执行完之后再调用某些操作。有很多异步接口的实现:

  • 回调函数
  • 返回一个占位符(Future, Promise,Defered)
  • 传送给队列
  • 信号机制

不管采用哪种异步方式,异步函数与调用者的交互都不是同步的。

示例

下面是一个简单的同步函数:

from tornado.httpclient import HTTPClient

def synchronous_fetch(url):
    http_client = HTTPClient()
    response = http_client.fetch(url)
    return response.body

下面是使用回调改写成异步函数的版本:

from tornado.httpclient import AsyncHTTPClient

def asynchronous_fetch(url, callback):
    http_client = AsyncHTTPClient()
    def handle_response(response):
        callback(response.body)
    http_client.fetch(url, callback=handle_response)

使用Future替换回调:

from tornado.concurrent import Future

def async_fetch_future(url):
    http_client = AsyncHTTPClient()
    my_future = Future()
    fetch_future = http_client.fetch(url)
    fetch_future.add_done_callback(
        lambda f: my_future.set_result(f.result()))
    return my_future

原始的Future版本更为复杂,尽管如此,还是推荐在Tornado中使用Future,因为它有两个优点:

  • 错误处理更为一致,因为Future.result可以抛出异常。
  • Future在协程中使用非常方便。

协程在后面会重点介绍,下面是采用协程方式编写的代码:

from tornado import gen

@gen.coroutine
def fetch_coroutine(url):
    http_client = AsyncHTTPClient()
    response = yield http_client.fetch(url)
    raise gen.Return(response.body)

使用raise gen.Return(response.body)是为了兼容Python2,因为Python2中生成器不允许返回值,为了克服这一点,Tornado协程抛出了一种特殊的异常Return,协程会捕获这个异常,然后将它当做返回值处理,在Python3中,可以直接使用return response.body

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏nummy

Tornado入门(四)【Tornado队列实例】

Tornado的tornado.queue采用协程实现了异步的生产者/消费者模式,跟Python内置的通过线程实现的queue模块功能类似。

16330
来自专栏nummy

Tornado入门(八)【运行和部署】

由于Tornado自身提供了HTTPServer, 所以它的运行和部署与其他Python Web 框架不一样。我们可以直接写一个main()方法来启动一个服务器...

17620
来自专栏nummy

Tornado入门(一)【简介】

Tornado是基于Python实现的异步网络框架,它采用非阻塞IO,可以支持成千上万的并发访问量,所以非常适合于长轮询和Websocket,以及其他需要持久连...

25820
来自专栏MoeLove

[译]Tornado协程

Tornado 4.3于2015年11月6日发布,该版本正式支持Python3.5的async/await关键字,并且用旧版本CPython编译Tornado同...

12220
来自专栏nummy

Tornado入门(三)【协程】

在Tornado中,协程是推荐使用的异步方式。协程使用yield关键字暂停或者恢复执行,而不是回调链的方式。

20530
来自专栏Python攻城狮

Python Web 框架:Tornado1.Tornado2.安装3.使用4.Tornado 代码解析

备注: Tornado应该运行在类Unix平台,为了达到最佳的性能和扩展性,仅推荐Linux和BSD(充分利用Linux的epoll工具和BSD的kqueue...

11230
来自专栏nummy

Tornado入门(六)【模板和UI】

Tornado也可以使用其他任意的模板引擎, 尽管并没有明确规则如何在RequestHandler.render整合进这些引擎。实际上只需要将模板渲染成字符串,...

13710
来自专栏MoeLove

[译]Tornado异步非阻塞I/O

Tornado 4.3于2015年11月6日发布,该版本正式支持Python3.5的async/await关键字,并且用旧版本CPython编译Tornado同...

14220
来自专栏nummy

Tornado入门(七)【认证和安全】

Cookies信息通常不安全,很容易被篡改。如果需要通过cookies来区分不同的登录用户,则需要对cookies进行签名,以防伪造。Tornado通过set_...

11120

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励