同步异步I/O客户端

from tornado.httpclient import HTTPClient,AsyncHTTPClient

def ssync_visit():
http_client = HTTPClient()
response = http_client.fetch('www.baidu.com') # 阻塞,直到网站请求完成
print(response.body) def hendle_response(response):
print(response.body)
def async_visit():
http_client = AsyncHTTPClient()
http_client.fetch('www.baidu.com',callback=hendle_response) # 非阻塞 async_visit()

协程

1、编写协程函数

from tornado import gen # 引入协程库

from tornado.httpclient import AsyncHTTPClient

@gen.coroutine
def coroutine_visit():
http_client = AsyncHTTPClient()
response = yield http_client.fetch('www.baidu.com')
print(response.body)

2、调用协程函数

由于Tornado协程基于python的yield关键字实现,所以不能调用普通函数一样调用协程函数

协程函数可通过以下三种方式调用

  • 在本身是协程的函数内通过yield关键字调用
  • 在IOLoop尚未启动时,通过IOLoop的run_sync()函数调用
  • 在IOLoop已经启动时,通过IOLoop的spawn_callback()函数调用

  下面是一个通过协程函数调用协程函数的例子

@gen.coroutine
def outer_coroutine():
print('开始调用另一个协程')
yield coroutine_visit()
print('outer_coroutine 调用结束')
outer_coroutine和coroutine_visit都是协程函数,他们之间可以通过yield关键字进行调用

IOLoop 是Tornado的主事件循环对象,Tornado程序通过它监听外部客户端的访问请求,并执行相应的操作,当程序尚未进入IOLoop的runing状态时,可以通过run_sync()函数调用协程函数,比如:
from tornado import gen # 引入协程库
from tornado.ioloop import IOLoop
from tornado.httpclient import AsyncHTTPClient @gen.coroutine
def coroutine_visit():
http_client = AsyncHTTPClient()
response = yield http_client.fetch('http://www.baidu.com')
print(response.body) def func_normal():
print('开始调用协程')
IOLoop.current().run_sync(lambda: coroutine_visit())
print('结束协程调用')
func_normal()

本例中run_sync()函数将当前函数的执行进行阻塞,直到被调用的协程执行完成

Tornado 要求协程函数在IOloop的running状态才能被调用,只不过run_sync函数自动完成了启动,停止IOLoop的步骤,他的实现逻辑为:启动IOLoop-调用被lambda封装的协程函数-停止IOLoop

当tornado程序已经处于running 状态时协程的调用如下:

def func_normal():
print('开始调用协程')
IOLoop.current().spawn_callback(coroutine_visit)
print('结束协程调用')
func_normal()
开始调用协程
结束协程调用

本例中spawn_callback函数不会等待被调用的协程执行完成,而协程函数将会由IOLoop在合适的时机进行调用,并且spawn_callback函数没有提供电泳返回值的方法,所以hi能用该函数调用没有返回值的协程函数

3、在协程中调用阻塞函数

在协程中直接调用阻塞函数会影响协程本身的性能,所以tornado提供了在协程中利用线程池调度阻塞函数,从而不影响协程本身继续执行的方法,实例代码如下:

from concurrent.futures import ThreadPoolExecutor
from tornado import gen
thread_pool = ThreadPoolExecutor(2) def mySleep(count):
import time
for i in range(count):
time.sleep(1) @gen.coroutine
def call_backing():
print('开始调用当前函数')
yield thread_pool.submit(mySleep,10)
print('结束调用') call_backing()

4、在协程中的等待多个异步调用

tornado允许在协程中用一个yield关键字等待多个异步调用,只需把这些调用用列表或字典的方式传递给yield关键字即可

实例如下:

from tornado import gen # 引入协程库
from tornado.ioloop import IOLoop
from tornado.httpclient import AsyncHTTPClient @gen.coroutine
def coroutine_visit():
http_client = AsyncHTTPClient()
list_response = yield [http_client.fetch('http://www.baidu.com'),
http_client.fetch('http://www.sina.com'),
http_client.fetch('http://www.163.com')
]
for response in list_response:
print(response.body) def func_normal():
print('开始调用协程')
IOLoop.current().run_sync(lambda: coroutine_visit())
print('结束协程调用')
func_normal()

字典同理,不再演示

Tornado 网站

异步化,协程化

当大量客户端高并发请求场景出现时,需要用到两种方式改变同步的处理请求流程

  • 异步化:针对RequestHandler的处理函数使用@tornado.web.asynchronous修饰器,将默认同步机制改成异步机制
  • 协程化:针对RequestHandler的处理函数使用@tornado.gen.coroutine修饰器,将默认的同步机制还成协程机制

1、异步化

from tornado import web,httpclient
import tornado
class MainHandler(tornado.web.RequestHandler): @tornado.web.asynchronous
def get(self):
http = tornado.httpclient.AsyncHTTPClient()
http.fetch('http://www.baidu.com',callback=self.on_response)
def on_response(self,response):
if response.error:
raise tornado.web.HTTPError(500)
self.write(response.body)
self.finish()

用@tornado.web.asynchronous 定义HTTP访问处理函数get(),当get函数返回时对该访问的请求尚未完成,所以tornado无法发送响应给客户端,只有随后的回掉函数中的finsh函数被调用时,tornado才知道本次处理已经完成,可以发送响应给客户端

异步虽然提高了并发能力,但是编程方法更繁琐

2、协程化

tornado 协程结合同步异步的优点,

import tornado.web
import tornado.httpclient
class MainHandler(tornado.web.RequestHandler): @tornado.gen.coroutine
def get(self):
http = tornado.httpclient.AsyncHTTPClient()
response = yield http.fetch('http://www.baidu.com')
self.write(response.body)

用tornado.gen.coroutine装饰MainHandler的get(),post()函数

使用异步对象处理耗时操作,比如AsyncHTTPClient

调用yield关键字获取异步对象的处理结果

实践中的异步

下各项同步(阻塞)的,如果在 tornado 中按照之前的方式只用它们,就是把 tornado 的非阻塞、异步优势削减了。

  • 数据库的所有操作,不管你的数据是 SQL 还是 noSQL,connect、insert、update 等
  • 文件操作,打开,读取,写入等
  • time.sleep
  • smtplib,发邮件的操作
  • 一些网络操作,比如 tornado 的 httpclient 以及 pycurl 等

解决方法

  • 在数据库方面,由于种类繁多,不能一一说明,比如 mysql,可以使用adb模块来实现 python 的异步 mysql 库;对于 mongodb 数据库,有一个非常优秀的模块,专门用于在 tornado 和 mongodb 上实现异步操作,它就是 motor。
  • 文件操作方面也没有替代模块,只能尽量控制好 IO,或者使用内存型(Redis)及文档型(MongoDB)数据库。
  • time.sleep() 在 tornado 中有替代:yield tornado.gen.sleep() 或者tornado.ioloop.IOLoop.instance().add_timeout
  • smtp 发送邮件,推荐改为 tornado-smtp-client。
  • 对于网络操作,要使用 tornado.httpclient.AsyncHTTPClient。

greenlet-tornado 可以实现用专门的库来实现tornado 的异步而不使用装饰器的异步

Tornado 协程的更多相关文章

  1. tornado用户指引(三)------tornado协程使用和原理(二)

    Python3.5  async和await async和await是python3.5引入的2个新的关键字(用这两个关键字编写的函数也称之为"原生协程"). 从tornado4. ...

  2. tornado用户指引(二)------------tornado协程实现原理和使用(一)

    摘要:Tornado建议使用协程来实现异步调用.协程使用python的yield关键字来继续或者暂停执行,而不用编写大量的callback函数来实现.(在linux基于epoll的异步调用中,我们需要 ...

  3. tornado 协程的实现原理个人理解;

    tornado实现协程的原理主要是利用了(1)python里面的generator (2)future类和ioloop相互配合,两者之间的相互配合是通过gen.coroutine装饰器来实现的: 具体 ...

  4. tornado用户指引(四)------tornado协程使用和原理(三)

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/happyAnger6/article/details/51291221几种常用的协程方式: 1.回调 ...

  5. Python协程 Gevent Eventlet Greenlet

    https://zh.wikipedia.org/zh-cn/%E5%8D%8F%E7%A8%8B 协程可以理解为线程中的微线程,通过手动挂起函数的执行状态,在合适的时机再次激活继续运行,而不需要上下 ...

  6. 分析Tornado的协程实现

    转自:http://www.binss.me/blog/analyse-the-implement-of-coroutine-in-tornado/ 什么是协程 以下是Wiki的定义: Corouti ...

  7. 深入tornado中的协程

    tornado使用了单进程(当然也可以多进程) + 协程 + I/O多路复用的机制,解决了C10K中因为过多的线程(进程)的上下文切换 而导致的cpu资源的浪费. tornado中的I/O多路复用前面 ...

  8. python tornado TCPserver异步协程实例

    项目所用知识点 tornado socket tcpserver 协程 异步 tornado tcpserver源码抛析 在tornado的tcpserver文件中,实现了TCPServer这个类,他 ...

  9. Python开发【模块】:tornado.queues协程的队列

    协程的队列 协调生产者消费者协程. from tornado import gen from tornado.ioloop import IOLoop from tornado.queues impo ...

随机推荐

  1. GitHub图形界面使用笔记

    GitHub图形界面使用笔记   学会了最简单的在GitHub上上传项目和展示项目,怕自己会忘记所以还是先记录下了来. GitHub 是一个共享虚拟主机服务,用于存放使用Git版本控制的软件代码和内容 ...

  2. Postgresql与Oralce常用用法区别总结

    日期操作 1. 操作当前日期和时间 oracle Select SYSDATE FROM dual; PostgreSQL Select CURRENT_DATE; Select NOW();返回日期 ...

  3. Dockerfile 中的 multi-stage

    在应用了容器技术的软件开发过程中,控制容器镜像的大小可是一件费时费力的事情.如果我们构建的镜像既是编译软件的环境,又是软件最终的运行环境,这是很难控制镜像大小的.所以常见的配置模式为:分别为软件的编译 ...

  4. LuoguP3701 「伪模板」主席树

    题面 这个题很有意思啊... 其实是道最大流板子题,只连byx会赢的边,S向byx连,另一个连T... 注意有长者时连的边加上同方mogician的个数... 还要注意mogician可以无限续命,也 ...

  5. jdbc连接数据库并打印的简单例子

    6步连接数据库: import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; impor ...

  6. Solidity by Example详解 - Voting

    Example地址:https://solidity.readthedocs.io/en/develop/solidity-by-example.html#voting Voting程序的功能: 这个 ...

  7. FCN小小实战

    先说一下前期准备工作:自己的运行环境是Ubuntu16.04+caffe+CPU(这台电脑没有GPU)+python 关于python的搭建就不说了,网上随便一搜,很多参考资源.说一下我配置好caff ...

  8. [.Net Core] 简单使用 Mvc 内置的 Ioc(续)

    简单使用 Mvc 内置的 Ioc(续) 本文基于 .NET Core 2.0. 上一章<[.Net Core] 简单使用 Mvc 内置的 Ioc>已经对日常 Mvc 中的 Ioc 的简单用 ...

  9. libpqxx接口的在linux下的使用,解决psql:connections on Unix domain socket "/tmp/.s.PGSQL.5432"错误

    在项目中使用postgresql数据库时要求在windows和linux双平台兼容.于是在windows下使用的接口在linux下爆出异常: psql:connections on Unix doma ...

  10. loadrunner录制上传文件,但是回放失败

    用Loadrunner录制上传文件,脚本回放时发现,文件没有上传成功,检查脚本发现脚本中没有上传文件的路径. 脚本录制时选择的协议,如果说是socket协议,则不需要上传文件的路径,Loadrunne ...