文档章节

使用tornado的coroutine进行编程

mickelfeng
 mickelfeng
发布于 2017/02/28 18:03
字数 1605
阅读 5
收藏 0
点赞 0
评论 0

在tornado3发布之后,强化了coroutine的概念,在异步编程中,替代了原来的gen.engine, 变成现在的gen.coroutine。这个装饰器本来就是为了简化在tornado中的异步编程。避免写回调函数, 使得开发起来更加符合正常逻辑思维。一个简单的例子如下:

 

class MaindHandler(web.RequestHandler):

   @asynchronous

   @gen.coroutine

   def post(self):

       client = AsyncHTTPClient()

       resp = yield client.fetch(https://api.github.com/users")

       if resp.code == 200:

           resp = escape.json_decode(resp.body)

           self.write(json.dumps(resp, indent=4, separators=(',', ':')))

       else:

           resp = {"message": "error when fetch something"}

           self.write(json.dumps(resp, indent=4, separators={',', ':')))

       self.finish()

在yield语句之后,ioloop将会注册该事件,等到resp返回之后继续执行。这个过程是异步的。在这里使用json.dumps,而没有使用tornado自带的escape.json_encode,是因为在构建REST风格的API的时候,往往会从浏览器里访问获取JSON格式的数据。使用json.dumps格式化数据之后,在浏览器端显示查看的时候会更加友好。Github API就是这一风格的使用者。其实escape.json_encode就是对json.dumps的简单包装,我在提pull request要求包装更多功能的时候,作者的回答escape并不打算提供全部的json功能,使用者可以自己直接使用json模块。

Gen.coroutine原理

在之前一篇博客中讲到要使用tornado的异步特性,必须使用异步的库。否则单个进程阻塞,根本不会达到异步的效果。 Tornado的异步库中最常用的就是自带的AsyncHTTPClient,以及在其基础上实现的OpenID登录验证接口。另外更多的异步库可以在这里找到。包括用的比较多的MongoDB的Driver。

在3.0版本之后,gen.coroutine模块显得比较突出。coroutine装饰器可以让本来靠回调的异步编程看起来像同步编程。其中便是利用了Python中生成器的Send函数。在生成器中,yield关键字往往会与正常函数中的return相比。它可以被当成迭代器,从而使用next()返回yield的结果。但是生成器还有另外一个用法,就是使用send方法。在生成器内部可以将yield的结果赋值给一个变量,而这个值是通过外部的生成器client来send的。举一个例子:

def test_yield():

   pirnt "test yeild"

   says = (yield)

   print says

 

if __name__ == "__main__":

   client = test_yield()

   client.next()

   client.send("hello world")

输出结果如下:

 

test yeild

hello world

已经在运行的函数会挂起,直到调用它的client使用send方法,原来函数继续运行。而这里的gen.coroutine方法就是异步执行需要的操作,然后等待结果返回之后,再send到原函数,原函数则会继续执行,这样就以同步方式写的代码达到了异步执行的效果。

Tornado异步编程

使用coroutine实现函数分离的异步编程。具体如下:

@gen.coroutine

def post(self):

   client = AsyncHTTPClient()

   resp = yield client.fetch("https://api.github.com/users")

   if resp == 200:

       body = escape.json_decode(resy.body)

   else:

       body = {"message": "client fetch error"}

       logger.error("client fetch error %d, %s" % (resp.code, resp.message))

   self.write(escape.json_encode(body))

   self.finish()

换成函数之后可以变成这样;

 

@gen.coroutime

def post(self):

   resp = yield GetUser()

   self.write(resp)

 

@gen.coroutine

def GetUser():

   client = AsyncHTTPClient()

   resp = yield client.fetch("https://api.github.com/users")

   if resp.code == 200:

       resp = escape.json_decode(resp.body)

   else:

       resp = {"message": "fetch client error"}

       logger.error("client fetch error %d, %s" % (resp.code, resp.message))

   raise gen.Return(resp)

这里,当把异步封装在一个函数中的时候,并不是像普通程序那样使用return关键字进行返回,gen模块提供了一个gen.Return的方法。是通过raise方法实现的。这个也是和它是使用生成器方式实现有关的。

 

使用coroutine跑定时任务

 

Tornado中有这么一个方法:

 

tornado.ioloop.IOLoop.instance().add_timeout()

该方法是time.sleep的非阻塞版本,它接受一个时间长度和一个函数这两个参数。表示多少时间之后调用该函数。在这里它是基于ioloop的,因此是非阻塞的。该方法在客户端长连接以及回调函数编程中使用的比较多。但是用它来跑一些定时任务却是无奈之举。通常跑定时任务也没必要使用到它。但是我在使用heroku的时候,发现没有注册信用卡的话仅仅能够使用一个简单Web Application的托管。不能添加定时任务来跑。于是就想出这么一个方法。在这里,我主要使用它隔一段时间通过Github API接口去抓取数据。大自使用方法如下:

 

装饰器

 

 def sync_loop_call(delta=60 * 1000):

 """

 Wait for func down then process add_timeout

 """

     def wrap_loop(func):

         @wraps(func)

         @gen.coroutine

         def wrap_func(*args, **kwargs):

             options.logger.info("function %r start at %d" %

                                 (func.__name__, int(time.time())))

             try:

                 yield func(*args, **kwargs)

             except Exception, e:

                 options.logger.error("function %r error: %s" %

                                      (func.__name__, e))

             options.logger.info("function %r end at %d" %

                                 (func.__name__, int(time.time())))

             tornado.ioloop.IOLoop.instance().add_timeout(

                 datetime.timedelta(milliseconds=delta),

                 wrap_func)

         return wrap_func

     return wrap_loop

任务函数

 

 @sync_loop_call(delta=10 * 1000)

 def worker():

     """

     Do something

     """

添加任务

 

 if __name__ == "__main__":

 worker()

 app.listen(options.port)

 tornado.ioloop.IOLoop.instance().start()

这样做之后,当Web Application启动之后,定时任务就会随着跑起来,而且因为它是基于事件的,并且异步执行的,所以并不会影响Web服务的正常运行,当然任务不能是阻塞的或计算密集型的。我这里主要是抓取数据,而且用的是Tornado自带的异步抓取方法。

 

在sync_loop_call装饰器中,我在wrap_func函数上加了@gen.coroutine装饰器,这样就保证只有yeild的函数执行完之后,才会执行add_timeout操作。如果没有@gen.coroutine装饰器。那么不等到yeild返回,就会执行add_timeout了。

 

完整地例子可以参见我的Github,这个项目搭建在heroku上。用于展示Github用户活跃度排名和用户区域分布情况。可以访问Github-Data查看。由于国内heroku被墙,需要翻墙才能访问。

 

总结

 

Tornado是一个非阻塞的web服务器以及web框架,但是在使用的时候只有使用异步的库才会真正发挥它异步的优势,当然有些时候因为App本身要求并不是很高,如果不是阻塞特别严重的话,也不会有问题。另外使用coroutine模块进行异步编程的时候,当把一个功能封装到一个函数中时,在函数运行中,即使出现错误,如果没有去捕捉的话也不会抛出,这在调试上显得非常困难。

本文转载自:http://www.pythontab.com/html/2013/pythonweb_0621/457.html

共有 人打赏支持
mickelfeng

mickelfeng

粉丝 226
博文 964
码字总数 548726
作品 0
成都
高级程序员
tornado异步请求的理解

官网第一段话: Tornado is a Python web framework and networking library, originally developed at FriendFeed. By using non-blocking network I/O, Tornado can scale to tens of thou......

okker ⋅ 2013/12/11 ⋅ 1

44.用Tornado实现web界面爬虫

用Tornado实现web界面爬虫 准备 Tornado 不管大家认不认识Tornado,还是简要地介绍一下吧。 Tornado是Facebook的一个开源的高性能Web服务器,最大的优势在于它基于事件的设计,使得它在IO密集...

quanpower ⋅ 2013/08/08 ⋅ 0

如何利用python tornado吃透web服务器

based on tornado 4.5.2 1999 年,Dan Kegel 向网络服务器提出了一个骇人听闻的难题:是时候让网络服务器去同时应对 10000 个客户端了,你觉得呢?毕竟网络已经变得很普及了。这就是著名的 C1...

Jun_Wong ⋅ 2017/11/14 ⋅ 0

tornado异步非阻塞处理请求

【tornado模块】 主要模块 - FriendFeed 使用的基础 Web 框架,包含了 Tornado 的大多数重要的功能 - XHTML, JSON, URL 的编码/解码方法 - 对 的简单封装,使其更容易使用 - 基于 Python 的 ...

SibylY ⋅ 2016/06/21 ⋅ 0

tornado常见的异步非堵塞写法

非堵塞和异步有什么区别? 非堵塞 在tornado的框架中非堵塞一般指得是网络I/O层面的socket数据接收模式(select或者epoll),不论用哪个模式,最终程序都会收到数据并处理数据(这个数据要么被...

极光火狐狸 ⋅ 2016/07/21 ⋅ 2

tornado 源码初识

序言 ioloop 源码分析 1.回调 callbacks 他是回调的基础部分,通过添加到他们将在每一次中被运行. 主要用途是将逻辑分块,在适合时机将包装好的k添加到让其执行. 例如中的 对象得到的时候会调用...

国夫君 ⋅ 2015/07/08 ⋅ 7

tornado线程阻塞的解决

前言 也许有同学很迷惑:tornado不是标榜异步非阻塞解决10K问题的嘛?但是我却发现不是torando不好,而是你用错了. 比如最近发现一个事情:某网站打开页面很慢,服务器cpu/内存都正常.网络状态也...

单蛙 ⋅ 2016/06/17 ⋅ 0

使用tornado让你的请求异步非阻塞

前言 也许有同学很迷惑:tornado不是标榜异步非阻塞解决10K问题的嘛?但是我却发现不是torando不好,而是你用错了.比如最近发现一个事情:某网站打开页面很慢,服务器cpu/内存都正常.网络状态也良...

jack_cheng ⋅ 2014/02/26 ⋅ 1

实践,用tornado实现自定义协议server

前言 俗话说"光说不练假把式",上一篇文里都只是光看着别人的源码说,貌似有点纸上谈兵的意思.所以这次写一个简单的,自己定义协议的.既可以熟悉的用法,又可以在去除了复杂的http协议后,了解的工...

国夫君 ⋅ 2015/07/31 ⋅ 0

从python协程理解tornado异步

博客原文地址:http://www.v2steve.com/2015/05/31/python/pytornadoasync/ 刚接触tornado时候最疑惑的问题就是tornado.gen.coroutine是怎么实现的。如何在代码中用同步格式实现异步效果。看...

__Steve__ ⋅ 2015/05/31 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Confluence 6 从其他备份中恢复数据

一般来说,Confluence 数据库可以从 Administration Console 或者 Confluence Setup Wizard 中进行恢复。 如果你在恢复压缩的 XML 备份的时候遇到了问题,你还是可以对整个站点进行恢复的,如...

honeymose ⋅ 7分钟前 ⋅ 0

myeclipse10 快速搭建spring boot开发环境(入门)

1.创建一个maven的web项目 注意上面标红的部分记得选上 2.创建的maven目录结构,有缺失的目录可以自己建立目录补充 补充后 这时候一个maven的web项目创建完成 3.配置pom.xml配置文件 <proje...

小海bug ⋅ 19分钟前 ⋅ 0

nginx.conf

=========================================================================== nginx.conf =========================================================================== user nobody; #......

A__17 ⋅ 22分钟前 ⋅ 0

645. Set Mismatch - LeetCode

Question 645. Set Mismatch Solution 思路: 遍历每个数字,然后将其应该出现的位置上的数字变为其相反数,这样如果我们再变为其相反数之前已经成负数了,说明该数字是重复数,将其将入结果r...

yysue ⋅ 35分钟前 ⋅ 0

Python这么强?红包杀手、消息撤回也可以无视,手机App辅助!

论述 标题也许有点不好理解,其实就是一款利用Python实现的可以监控微信APP内的红包与消息撤回的助手。不得不说,这确实是一款大家钟意的神器。 消息撤回是一件很让人恶心的事,毕竟人都是有...

Python燕大侠 ⋅ 51分钟前 ⋅ 0

压缩打包介绍、gzip压缩工具、bzip2压缩工具、xz压缩工具

压缩打包介绍 压缩的好处不仅能节省磁盘空间而且在传输的时候节省传输时间和网络带宽 windows系统下文件带有 .rar .zip .7z 后缀的就是压缩文件 linux系统下则是 .zip, .gz, .bz2, .xz, ...

黄昏残影 ⋅ 56分钟前 ⋅ 0

观察者模式

1.利用java原生类进行操作 package observer;import java.util.Observable;import java.util.Observer;/** * @author shadow * @Date 2016年8月12日下午7:29:31 * @Fun 观察目标 **/......

Cobbage ⋅ 59分钟前 ⋅ 0

Ubuntu打印服务器配置

参考:https://blog.csdn.net/gsls200808/article/details/50950586 https://blog.csdn.net/jiay2/article/details/80252369 https://wiki.gentoo.org/wiki/HPLIP 由于媳妇儿要大量打印资料,......

大熊猫 ⋅ 今天 ⋅ 0

面试的角度诠释Java工程师(二)

原文出处: locality 续言: 相信每一位简书的作者,都会有我这样的思考:怎么写好一篇文章?或者怎么写好一篇技术类的文章?我就先说说我的感悟吧,写文章其实和写程序是一样的。为什么我会...

颖伙虫 ⋅ 今天 ⋅ 0

github中SSH的Key

https://help.github.com/articles/connecting-to-github-with-ssh/ https://help.github.com/articles/testing-your-ssh-connection/ https://help.github.com/articles/adding-a-new-ssh-k......

whoisliang ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部