why asynchronous

tornado是一个异步web framework,说是异步,是因为tornado server与client的网络交互是异步的,底层基于io event loop。但是如果client请求server处理的handler里面有一个阻塞的耗时操作,那么整体的server性能就会下降。

def MainHandler(tornado.web.RequestHandler):
def get(self):
client = tornado.httpclient.HttpClient()
response = client.fetch("http://www.google.com/")
self.write('Hello World')

在上面的例子中,tornado server的整体性能依赖于访问google的时间,如果访问google的时间比较长,就会导致整体server的阻塞。所以,为了提升整体的server性能,我们需要一套机制,使得handler处理都能够通过异步的方式实现。

幸运的是,tornado提供了一套异步机制,方便我们实现自己的异步操作。当handler处理需要进行其余的网络操作的时候,tornado提供了一个async http client用来支持异步。

def MainHandler(tornado.web.RequestHandler):
@tornado.web.asynchronous
def get(self):
client = tornado.httpclient.AsyncHTTPClient()
def callback(response):
self.write("Hello World")
self.finish() client.fetch("http://www.google.com/", callback)

上面的例子,主要有几个变化:

  • 使用asynchronous decorator,它主要设置_auto_finish为false,这样handler的get函数返回的时候tornado就不会关闭与client的连接。
  • 使用AsyncHttpClient,fetch的时候提供callback函数,这样当fetch http请求完成的时候才会去调用callback,而不会阻塞。
  • callback调用完成之后通过finish结束与client的连接。

asynchronous flaw

异步操作是一个很强大的操作,但是它也有一些缺陷。最主要的问题就是在于callback导致了代码逻辑的拆分。对于程序员来说,同步顺序的想法是一个很自然的习惯,但是异步打破了这种顺序性,导致代码编写的困难。这点,对于写nodejs的童鞋来说,可能深有体会,如果所有的操作都是异步,那么最终我们的代码可能写成这样:

def MainHandler(tornado.web.RequestHandler):
@tornado.web.asynchronous
def get(self):
client = tornado.httpclient.AsyncHTTPClient()
def callback1(response): def callback2(response):
self.write("Hello World")
self.finish()
client.fetch("http://www.google.com", callback2) client.fetch("http://www.google.com/", callback1)

也就是说,我们可能会写出callback嵌套callback的情况,这个极大的会影响代码的阅读与流程的实现。

synchronous

我个人认为,异步拆散了代码流程这个问题不大,毕竟如果一个逻辑需要过多的嵌套callback来实现的话,那么我们就需要考虑这个逻辑是否合理了,所以异步一般也不会有过多的嵌套层次。

虽然我认为异步的callback问题不大,但是如果仍然能够有一套机制,使得异步能够顺序化,那么对于代码逻辑的编写来说,会方便很多。tornado有一些机制来实现。

yield

在python里面如果一个函数内部实现了yield,那么这个函数就不是函数了,而是一个生成器,它的整个运行机制也跟普通函数不一样,举一个例子:

def test_yield():
print 'yield 1'
a = yield 'yielded'
print 'over', a t = test_yield()
print 'main', type(t)
ret = t.send(None)
print ret
try:
t.send('hello yield')
except StopIteration:
print 'yield over'

输出结果如下:

main <type 'generator'>
yield 1
yielded
over hello yield
yield over

从上面可以看到,test_yield是一个生成器,当它第一次调用的时候,只是生成了一个Generator,不会执行。当第一次调用send的时候,生成器被resume,开始执行,然后碰到yield,就挂起,等待下一次被send唤醒。当生成器执行完毕,会抛出StopIteration异常,供外部send的地方知晓。

因为yield很方便的提供了一套函数挂起,运行的机制,所以我们能够通过yield来将原本是异步的流程变成同步的。

gen

tornado有一个gen模块,提供了Task和Callback/Wait机制用来支持同步模型,以task为例:

def MainHandler(tornado.web.RequestHandler):
@tornado.web.asynchronous
@tornado.gen.engine
def get(self):
client = tornado.httpclient.AsyncHTTPClient()
response = yield tornado.gen.Task(client.fetch, "http://www.google.com/")
self.write("Hello World")
self.finish()

可以看到,tornado的gen模块就是通过yield来进行同步化的。主要有如下需要注意的地方:

  • 使用gen.engine的decorator,该函数主要就是用来管理generator的流程控制。
  • 使用了gen.Task,在gen.Task内部,会生成一个callback函数,传给async fetch,并执行fetch,因为fetch是一个异步操作,所以会很快返回。
  • 在gen.Task返回之后使用yield,挂起
  • 当fetch的callback执行之后,唤醒挂起的流程继续执行。

可以看到,使用gen和yield之后,原先的异步逻辑变成了同步流程,在代码的阅读性上面就有不错的提升,不过对于不熟悉yield的童鞋来说,开始反而会很迷惑,不过只要理解了yield,那就很容易了。

greenlet

虽然yield很强大,但是它只能挂起当前函数,而无法挂起整个堆栈,这个怎么说呢,譬如我想实现下面的功能:

def a():
yield 1 def b():
a() t = b()
t.send(None)

这个通过yield是无法实现的,也就是说,a里面使用yield,它是一个生成器,但是a的挂起无法将b也同时挂起。也就是说,我们需要一套机制,使得堆栈在任何地方都能够被挂起和恢复,能方便的进行栈切换,而这套机制就是coroutine。

最开始使用coroutine是在lua里面,它原生提供了coroutine的支持。然后在使用luajit的时候,发现内部是基于fiber(win)和context(unix),也就是说,不光lua,其实c/c++我们也能实现coroutine。现在研究了go,也是内置coroutine,并且这里极力推荐一篇slide

python没有原生提供coroutine,不知道以后会不会有。但有一个greenlet,能帮我们实现coroutine机制。而且还有人专门写好了tornado与greenlet结合的模块,叫做greenlet_tornado,使用也很简单

class MainHandler(tornado.web.RequestHandler):
@greenlet_asynchronous
def get(self):
response = greenlet_fetch('http://www.google.com')
self.write("Hello World")
self.finish()

可以看到,使用greenlet,能更方便的实现代码逻辑,这点比使用gen更方便,因为这些连写代码的童鞋都不用去纠结yield问题了。

总结

这里只是简单的介绍了tornado的一些异步处理流程,以及将异步同步化的一些方法。另外,这里举得例子都是网络http请求方面的,但是server处理请求的时候,可能还需要进行数据库,本地文件的操作,而这些也是同步阻塞耗时操作,同样可以通过异步来解决的,这里就不详细说明了。

学习tornado:异步的更多相关文章

  1. 5.(基础)tornado异步

    终于到了传说中的异步了,感觉异步这个名字听起来就很酷酷的,以前还不是多擅长Python时,就跑去看twisted的源码,结果给我幼小的心灵留下了创伤.反正包括我在内,都知道异步编程很强大,但是却很少在 ...

  2. 7.2 Tornado异步

    7.2 Tornado异步 因为epoll主要是用来解决网络IO的并发问题,所以Tornado的异步编程也主要体现在网络IO的异步上,即异步Web请求. 1. tornado.httpclient.A ...

  3. tornado异步请求的理解(转)

    tornado异步请求的理解 http://www.kankanews.com/ICkengine/archives/88953.shtml 官网第一段话: Tornado is a Python w ...

  4. 使用Tornado异步接入第三方(支付宝)支付

    目前国内比较流行的第三方支付主要有支付宝和微信支付,博主最近研究了下如何用Python接入支付宝支付,这里我以Tornado作为web框架,接入支付宝构造支付接口. 使用Tornado异步接入支付宝支 ...

  5. Tornado异步非阻塞的使用以及原理

    Tornado 和现在的主流 Web 服务器框架(包括大多数 Python 的框架)有着明显的区别:它是非阻塞式服务器,而且速度相当快.得利于其 非阻塞的方式和对 epoll 的运用,Tornado ...

  6. Python Web框架 tornado 异步原理

    Python Web框架 tornado 异步原理 参考:http://www.jb51.net/article/64747.htm 待整理

  7. [译]聊聊C#中的泛型的使用(新手勿入) Seaching TreeVIew WPF 可编辑树Ztree的使用(包括对后台数据库的增删改查) 字段和属性的区别 C# 遍历Dictionary并修改其中的Value 学习笔记——异步 程序员常说的「哈希表」是个什么鬼?

    [译]聊聊C#中的泛型的使用(新手勿入)   写在前面 今天忙里偷闲在浏览外文的时候看到一篇讲C#中泛型的使用的文章,因此加上本人的理解以及四级没过的英语水平斗胆给大伙进行了翻译,当然在翻译的过程中发 ...

  8. Tornado异步(2)

    Tornado异步 因为epoll主要是用来解决网络IO的并发问题,所以Tornado的异步编程也主要体现在网络IO的异步上,即异步Web请求. 1. tornado.httpclient.Async ...

  9. tornado异步(1)

    1. 同步 我们用两个函数来模拟两个客户端请求,并依次进行处理: # coding:utf-8 def req_a(): """模拟请求a""&quo ...

  10. 第一次学习tornado小练习

    内容 这次是python的一个web框架,tornado,这个web框架在python的几个web框架中一个比较简单的web框架,刚开始接触python的时候就知道python有两个比较常用的web框 ...

随机推荐

  1. 底部导航栏实现一 Fragment-replace

    [效果](这里下载的软件收费的试用有水印) [推荐]这里推荐一个图标网http://iconfont.cn/.以上图标来自此图标网 [项目结构] [步骤] ①创建布局文件,写底部导航栏 <?xm ...

  2. Jupyter Notebook 快速入门

    Jupyter Notebook(此前被称为 IPython notebook)是一个交互式笔记本,支持运行 40 多种编程语言.在本文中,我们将介绍 Jupyter notebook 的主要特性,以 ...

  3. 开机小脚本自动打开sublime text 和git-bash

    set subl="C:\Program Files (x86)\Sublime Text 3\subl.exe" set git-bash="C:\Program Fi ...

  4. 代理IP爬取,计算,发放自动化系统

    IoC Python端 MySQL端 PHP端 怎么使用 这学期有一门课叫<物联网与云计算>,于是我就做了一个大作业,实现的是对代理IP的爬取,计算推荐,发放给用户等任务的的自动化系统.由 ...

  5. Leetcode解题-链表(2.2.0)基础类

    1 基类的作用 在开始练习LeetCode链表部分的习题之前,首先创建好一个Solution基类,其作用就是: Ø  规定好每个子Solution都要实现纯虚函数test做测试: Ø  提供了List ...

  6. 剑指Offer——求职必备神器

    剑指Offer--求职必备神器 前言   不管是公司网申.银行招聘.面试等等,"谈谈你的职业规划"."以往工作中遇到了哪些棘手问题?你是如何解决的?".&quo ...

  7. 有N个数,组成的字符串,如012345,求出字串和取MOD3==0的子串,如012 12 123 45。

    #include <iostream> using namespace std; int getSum(string str, int begin, int len) { int sum ...

  8. How to generate the complex data regularly to Ministry of Transport of P.R.C by DB Query Analyzer

    How to generate the complex data regularly to Ministry of Transport of P.R.C by DB Query Analyzer 1 ...

  9. iOS下JS与OC互相调用(四)--JavaScriptCore

    前面讲完拦截URL的方式实现JS与OC互相调用,终于到JavaScriptCore了.它是从iOS7开始加入的,用 Objective-C 把 WebKit 的 JavaScript 引擎封装了一下, ...

  10. Swift变量名的一种玩法

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 是的,Swift的变量名可以用任何合法的Unicode字符,这 ...