资讯专栏INFORMATION COLUMN

tornado6与python3.7 异步新姿势

maxmin / 955人阅读

摘要:这是我重新复习的原因放弃了之前自己实现的全面拥抱的这个改动是非常大的而且阅读的源码可以发现其中大部分函数都支持了类型检验和返回值提示值得阅读

废话不多说,直接上代码
__auth__ = "aleimu"
__doc__ = "学习tornado6.0+ 版本与python3.7+"

import time
import asyncio
import tornado.gen
import tornado.web
import tornado.ioloop
import tornado.httpserver  # tornado的HTTP服务器实现
from tornado.options import define, options
from tornado.httpclient import HTTPClient, AsyncHTTPClient
from requests import get

settings = {"debug": True}
url = "http://127.0.0.1:5000/"  # 这是另个服务,请求5s后返回结果


# RuntimeError: Cannot run the event loop while another loop is running
# 解释:HTTPClient内部写 loop.run_xxx,因为那是启动event loop的命令,通常只再最最最外面用一次,之后的代码都应假设 loop 已经在运转了。
def synchronous_fetch(url):
    print("synchronous_fetch")
    try:
        http_client = HTTPClient()
        time.sleep(5)
        response = http_client.fetch(url)
        print(response.body)
    except Exception as e:
        print("Error: " + str(e))
        return str(e)
    http_client.close()
    return response.body


# 替代synchronous_fetch的同步请求,没有内置loop.run_xxx
def synchronous_get(url):
    response = get(url)
    time.sleep(5)
    print("synchronous_fetch")
    return response.text


# 简单的模拟异步操作,这里之后应该替换成各种异步库的函数
async def sleep():
    print("start sleep")
    await asyncio.sleep(5)
    print("end sleep")


# 异步请求
async def asynchronous_fetch(url):
    http_client = AsyncHTTPClient()
    response = await http_client.fetch(url)
    print("asynchronous_fetch")
    return response.body


# 测试
class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, world:%s" % self.request.request_time())
        self.finish()
        print("not finish!")
        return


# 同步阻塞
class synchronous_fetcher(tornado.web.RequestHandler):
    def get(self):
        self.write("%s,%s" % (synchronous_fetch(url), self.request.request_time()))


# 同步阻塞
class synchronous_geter(tornado.web.RequestHandler):
    def get(self):
        self.write("%s,%s" % (synchronous_get(url), self.request.request_time()))


# 异步阻塞,我以为curl "127.0.0.1:8888/1" 总耗时希望为5s,可是是25s,看来异步没搞好,以下的函数都是基于此改进的
class asynchronous_fetcher_1(tornado.web.RequestHandler):
    async def get(self):
        body = await asynchronous_fetch(url)
        for i in range(3):
            print("skip %s" % i)
            await tornado.gen.sleep(5)
        time.sleep(5)
        print("end request")
        self.write("%s,%s" % (body, self.request.request_time()))

# curl "127.0.0.1:8888/1"
# b"{
  "data": "123"
}
",25.026000022888184


# 异步阻塞,效果同上,这里只是证明 tornado.gen.sleep(5)和asyncio.sleep(5) 效果一致
class asynchronous_fetcher_2(tornado.web.RequestHandler):
    async def get(self):
        body = await asynchronous_fetch(url)  # 关注协程完成后返回的结果
        for i in range(3):
            print("skip %s" % i)
            await sleep()
        time.sleep(5)
        print("end request")
        self.write("%s,%s" % (body, self.request.request_time()))

# curl "127.0.0.1:8888/2"
# b"{
  "data": "123"
}
",25.039999961853027


# 异步非阻塞-将部分异步操作放入组中,实现loop管理
class asynchronous_fetcher_3(tornado.web.RequestHandler):
    async def get(self):
        body = await asynchronous_fetch(url)
        await asyncio.wait([sleep() for i in range(3)])
        print("end request")
        self.write("%s,%s" % (body, self.request.request_time()))

# curl "127.0.0.1:8888/3"
# b"{
  "data": "123"
}
",10.001000165939331

# 异步非阻塞-将所有异步操作放入组中,实现loop管理
class asynchronous_fetcher_4(tornado.web.RequestHandler):
    async def get(self):
        task_list = [sleep() for i in range(3)]
        task_list.append(asynchronous_fetch(url))
        body = await asyncio.wait(task_list)  # 将所有异步操作的结果返回,但是是无序的,要是需要返回结果的话解析起来比较麻烦
        print("end request:", body)
        # print(type(body), len(body),type(body[0]),len(body[0]),type(body[0]))
        self.write("%s,%s" % ([x.result() for x in body[0] if x.result() is not None][0],
                              self.request.request_time()))
# curl "127.0.0.1:8888/4"
# b"{
  "data": "123"
}
",5.006999969482422

def make_app():
    return tornado.web.Application([
        (r"/", MainHandler),
        (r"/1", asynchronous_fetcher_1),
        (r"/2", asynchronous_fetcher_2),
        (r"/3", asynchronous_fetcher_3),
        (r"/4", asynchronous_fetcher_4),
        (r"/5", synchronous_fetcher),
        (r"/6", synchronous_geter),

    ], **settings)


if __name__ == "__main__":
    print("server start!")
    app = make_app()
    server = tornado.httpserver.HTTPServer(app)
    server.bind(8888)
    server.start(1)  # forks one process per cpu,windows上无法fork,这里默认为1
    tornado.ioloop.IOLoop.current().start()
总结
1.Tornado使用单线程事件循环,写的不好,会阻塞的非常严重,比如synchronous_geter
2.flask+celery可以完成常见的异步任务
3.await语法只能出现在通过async修饰的函数中
4.可以看到tornado.gen.coroutine, tornado.concurrent.run_on_executor,tornado.web.asynchronous,tornado.gen.coroutine等这些装饰器都不在经常使用了,都由async和await代替
参考文档:
https://zhuanlan.zhihu.com/p/27258289   # Python async/await入门指南
http://www.tornadoweb.org/en/stable/guide/intro.html    # 这个官网
https://www.osgeo.cn/tornado/guide/intro.html   #Tornado 1.0 - Tornado 6.0的更新说明,以及6.0版本的中文文档,适合英语不好的人阅读
https://www.osgeo.cn/tornado/releases/v5.0.0.html#  在Python 3上, IOLoop 总是包装asyncio事件循环。

On Python 3, IOLoop is always a wrapper around the asyncio event loop.
这是我重新复习tornado的原因,tornado放弃了之前自己实现的tornado.ioloop,全面拥抱asyncio的event_loop.这个改动是非常大的,
而且阅读tornado的源码可以发现其中大部分函数都支持了类型检验,和返回值提示,值得阅读.

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/43783.html

相关文章

  • 异步读取文件的几种姿势

    摘要:臆想的针对读取到的内容进行操作,比如打印文件内容臆想中,读取文件是有返回值的,将返回值,即文件内容,赋给一个变量,然后决定对读取到的内容进行相应的操作,例如打印文件中的内容。 臆想的 let fs = require(fs) function readFile(filename){ ... } let content = readFile(config.js) // 针对读...

    chinafgj 评论0 收藏0
  • MongoDB 4.0 Python3.7 稳定高效的评分制IP代理池APIserver

    摘要:项目的主要运行部分,采集器验证器打分检测等功能实现的模块。在中可以配置异步的并发量等来控制验证器。调用有了稳定的高分代理数据,那么就可以挂起一个为我们的爬虫保驾护航,这一部分可以单独拿出来编写,使用其他框架之类的都是不错的选择。 FooProxy 稳健高效的评分制 IP代理池 + API服务提供,可以自己插入采集器进行代理IP的爬取,支持 MongoDB 4.0 使用 Python3....

    wangjuntytl 评论0 收藏0
  • MongoDB 4.0 Python3.7 稳定高效的评分制IP代理池APIserver

    摘要:项目的主要运行部分,采集器验证器打分检测等功能实现的模块。在中可以配置异步的并发量等来控制验证器。调用有了稳定的高分代理数据,那么就可以挂起一个为我们的爬虫保驾护航,这一部分可以单独拿出来编写,使用其他框架之类的都是不错的选择。 FooProxy 稳健高效的评分制 IP代理池 + API服务提供,可以自己插入采集器进行代理IP的爬取,支持 MongoDB 4.0 使用 Python3....

    AndroidTraveler 评论0 收藏0
  • 处理JavaScript异常的正确姿势

    摘要:我们使用单元测试来验证一下我们使用了配合做单元测试。我们编写相应的单元测试你会发现,如果出现异常,只是简单的返回。但是在上面异常抛出的时候,解释器已经不在中了,因此无法被捕获。 译者按: 错误是无法避免的,妥善处理它才是最重要的! 原文: A Guide to Proper Error Handling in JavaScript Related Topics: 译者: Funde...

    lushan 评论0 收藏0
  • 前端基础

    摘要:谈起闭包,它可是两个核心技术之一异步基于打造前端持续集成开发环境本文将以一个标准的项目为例,完全抛弃传统的前端项目开发部署方式,基于容器技术打造一个精简的前端持续集成的开发环境。 这一次,彻底弄懂 JavaScript 执行机制 本文的目的就是要保证你彻底弄懂javascript的执行机制,如果读完本文还不懂,可以揍我。 不论你是javascript新手还是老鸟,不论是面试求职,还是日...

    graf 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<