• Tornado 协程


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

  • 相关阅读:
    远程控制.scrcpy&其他资料&我的游戏辅助方案
    虚拟机.第三方.droid4x(海马玩)
    私.微信
    私.Modbus测试_ZC03_rtu,ascii,tcp
    Modbus资料
    私.Modbus测试_ZC02_串口方式
    私.Modbus测试_ZC01_TCP方式
    私.01.serialport
    C# Type Basics
    NORFLASH驱动详细说明
  • 原文地址:https://www.cnblogs.com/Erick-L/p/7068112.html
Copyright © 2020-2023  润新知