Tornado 的异步 怎么写的 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
aoscici2000
V2EX    Tornado

Tornado 的异步 怎么写的

  •  
  •   aoscici2000 2019-01-15 01:43:42 +08:00 11011 次点击
    这是一个创建于 2460 天前的主题,其中的信息可能已经有所发展或是发生改变。

    看得教程一头雾水, 只能理解那个自带的 http 请求, 看了很久也没看到有比如数据库查询, 文件处理什么的其他例子, 后来去看看其他教程, 实现倒算是勉强实现了, 但感觉也太复杂了吧? ?

    就比如例子中的我假设有个长耗时的任务, 怎么写才会简单点?

     import tornado.ioloop import tornado.web import time import _thread # 模拟耗时任务 def long_work(arg): time.sleep(5) yield arg * 1024 def mycoroutine(func): def wrapper(self): gen = func(self) work_gen = next(gen) def fun(): result = next(work_gen) try: gen.send(result) except StopIteration: pass _thread.start_new_thread(fun, ()) return wrapper class IndexHandler(tornado.web.RequestHandler): @tornado.web.asynchronous @mycoroutine def get(self): arg = 10 # 假设请求的参数 result = yield long_work(arg) self.finish(f'<h1>Index {result}</h1>') class SyncHandler(tornado.web.RequestHandler): def get(self): self.write('<h1>SyncHandler</h1>') def make_app(): return tornado.web.Application([ (r'/', IndexHandler), (r'/sync', SyncHandler) ]) if __name__ == '__main__': app = make_app() app.listen(8888) tornado.ioloop.IOLoop.current().start() 
    13 条回复    2019-05-27 16:00:35 +08:00
    Vegetable
        1
    Vegetable  
       2019-01-15 03:09:32 +08:00 via iPhone
    拥抱 asyncio 吧…
    asyncio 比 tornado 好理解的多,回头再来看 tornado 也好
    feisan
        2
    feisan  
       2019-01-15 03:09:38 +08:00
    1、尽量使用支持 tornado 异步 io 的库,比如用 tornado_mysql 访问 MySQL。
    2、配合 ThreadPoolExecutor 使用,把同步阻塞任务放到线程池。
    3、把同步阻塞的处理放到一个单独的进程,提供访问接口,然后通过非阻塞的方式去调用它。比如把数据库访问做成独立的 web 服务,然后在 tornado 里用 HTTP 去访问。或者把同步阻塞任务做成 celery 的 task,使用 tornado_celery 取调用。
    janxin
        3
    janxin  
       2019-01-15 10:10:22 +08:00
    time.sleep 是阻塞操作,因此你这里模拟耗时任务时也是阻塞的。最新的 Tornado 也是使用的 asyncio 驱动了,所以要么考虑一下了解 asyncio ?

    显示上下文切换(async/await/@coroutine)之类的,只有在声明处标示切换,如果某函数未切换(无 await)则仍旧是阻塞执行。另外虽然 yield 是在 Tornado 中用于切换,但是不代表用了就是异步行为,这里比较容易混淆。在使用 async/await 语法之后与普通 yield 行为区分开会更清晰。
    zhengxiaowai
        4
    zhengxiaowai  
       2019-01-15 11:08:48 +08:00
    @aoscici2000 https://hexiangyu.me/2017/01/29/real-tornado-async-noblocking/
    发现好多人不知道怎么用 tornado 写异步
    haozi3156666
        5
    haozi3156666  
       2019-01-15 11:31:27 +08:00
    数据库查询用 sqlalchemy 也行的,本身框架不带数据库操作相关模块的
    aoscici2000
        6
    aoscici2000  
    OP
       2019-01-15 12:48:09 +08:00
    @zhengxiaowai 文章倒是大部分看得懂, 但我发现可能大多新手也跟我一样, 迷惑点是耗时操作如何写成不堵的, 教程例子大多都是直接就用 gen.sleep, async.sleep , 关键是这两个东西是怎么实现的(源码也是看得稀里糊涂的)
    aoscici2000
        7
    aoscici2000  
    OP
       2019-01-15 12:53:33 +08:00
    @janxin 看过些, 感觉更难懂哈哈, 主要是很多实例直接就跳过了如何把堵塞的变成不堵的步骤, 我是卡在了这里...比如何如写一个不堵的函数?
    janxin
        8
    janxin  
       2019-01-15 13:43:47 +08:00
    @aoscici2000 看原理先,不要先看代码。asyncio 的代码质量没有特别高,而且很多边缘状况处理,先从源码很容易迷失。

    使用的第一步难道不是先知道我这个是不是异步的么?在 asyncio 中,标记了 coroutine 的都是异步非阻塞的,没标记的都是会阻塞的。先从正确使用开始,换句话说就是没有 await 的地方都有可能阻塞。
    aoscici2000
        9
    aoscici2000  
    OP
       2019-01-15 16:37:58 +08:00
    @janxin 头大就头大在第一步, 到底怎么写才算是非阻塞的, 其实很多教程的实例大致还能理解的, 就是这个 asyncio.sleep(x) 实在想知道它是怎么实现的. 例如下面这段的 work 是怎么样才能实现跟 asyncio.sleep 那样不阻塞得拿到完成的数据

    ```python
    async def work(sec):
    time.sleep(sec)
    return [i*100 for i in range(sec)]


    async def req_a(num):
    res = await work(num)
    print('in req_a:', res)


    async def req_b(num):
    res = await work(num)
    print('in req_b:', res)


    if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    tasks = [req_a(5), req_b(2)]
    loop.run_until_complete(asyncio.wait(tasks))
    print('All finished.')
    loop.close()
    ```
    coroutine
        10
    coroutine  
       2019-01-16 10:45:14 +08:00
    你应该先学习一下
    1. 事件驱动、IO 多路复用 的知识(CSAPP 和 UNP 里有讲)。
    比如先学习一下 select/poll 系统调用,OSX 下的 kqueue, 或者 Linux 下的 epoll(epoll_create, epoll_ctl, epoll_wait)的系统调用知识。
    2. Python 本身的 yield, 和 send 分别实现了函数运行时的挂起和唤醒,丢到双端队列里,配合事件驱动每次去取。

    然后再回头来看 asyncio 里是如何使用事件驱动的。比如你提到的 async.sleep. 实际就是下一次 epoll timeout 时的返回。

    ---

    Tornado 早期自己利用 epoll 写了事件驱动的源码, 前期也有替换 asyncio 的事件循环的代码: http://www.tornadoweb.org/en/stable/asyncio.html 后来的版本,**似乎**和 asyncio 做了兼容。

    另外,从写代码的角度,你可以把直接使用 async await 语法, 而不使用装饰器: http://www.tornadoweb.org/en/stable/guide/coroutines.html#native-vs-decorated-coroutines

    另外,《 Python Cookbook 》里也有使用事件驱动来实现同时 handle 多个请求的例子,都可以参考着学习一下。
    coroutine
        11
    coroutine  
       2019-01-16 10:46:15 +08:00
    asyncio.sleep 不是 async.sleep,打错
    coroutine
        12
    coroutine  
       2019-01-21 14:42:42 +08:00
    你如果确实有同步的库需要在异步环境执行,可以参考 https://stackoverflow.com/questions/22190403/how-could-i-use-requests-in-asyncio
    itwhat
        13
    itwhat  
       2019-05-27 16:00:35 +08:00
    py3 用 async 加 await,py2 就用协程装饰器(@gen.coroutine)加 yield
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1177 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 26ms UTC 23:39 PVG 07:39 LAX 16:39 JFK 19:39
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86