Tornado 协程

同步异步I/O客户端python

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()

协程mysql

一、编写协程函数git

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)

二、调用协程函数github

因为Tornado协程基于python的yield关键字实现,因此不能调用普通函数同样调用协程函数web

协程函数可经过如下三种方式调用sql

  • 在自己是协程的函数内经过yield关键字调用
  • 在IOLoop还没有启动时,经过IOLoop的run_sync()函数调用
  • 在IOLoop已经启动时,经过IOLoop的spawn_callback()函数调用

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

@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能用该函数调用没有返回值的协程函数

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

在协程中直接调用阻塞函数会影响协程自己的性能,因此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()

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

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修饰器,将默认的同步机制还成协程机制

一、异步化

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才知道本次处理已经完成,能够发送响应给客户端

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

二、协程化

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 的异步而不使用装饰器的异步

相关文章
相关标签/搜索