首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Tornado异步

Tornado异步

作者头像
星哥玩云
发布于 2022-09-14 11:14:03
发布于 2022-09-14 11:14:03
9450
举报
文章被收录于专栏:开源部署开源部署

一、说明

因为epoll主要是用来解决网络IO的并发问题,所以Tornado的异步编程也主要体现在网络IO的异步上,即异步Web请求

二、Tornado异步处理方法与类

  • tornado.httpclient.AsyncHTTPClient Tornado提供了一个异步Web请求客户端tornado.httpclient.AsyncHTTPClient用来进行异步Web请求
  • fetch(request, callback=None) 用于执行一个web请求request,并异步返回一个tornado.httpclient.HTTPResponse响应 request可以是一个url,也可以是一个tornado.httpclient.HTTPRequest对象。如果是url,fetch会自己构造一个HTTPRequest对象
  • HTTPRequest HTTP请求类,HTTPRequest的构造函数可以接收众多构造参数,最常用的如下
    • url(string) – 要访问的url,此参数必传,除此之外均为可选参数
    • method (string) – HTTP访问方式,如“GET”或“POST”,默认为GET方式
    • headers (HTTPHeaders or dict) – 附加的HTTP协议头
    • body – HTTP请求的请求体
  • HTTPResponse HTTP响应类,其常用属性如下:
    • code: HTTP状态码,如 200 或 404
    • reason: 状态码描述信息
    • body: 响应体字符串
    • error: 异常(可有可无)

三、tornado.web.asynchronous 回调异步

说明

此装饰器用于回调形式的异步方法,并且应该仅用于HTTP的方法上(如get、post等)

此装饰器不会让被装饰的方法变为异步,而只是告诉框架被装饰的方法是异步的,当方法返回时响应尚未完成。只有在request handler调用了finish方法后,才会结束本次请求处理,发送响应

不带此装饰器的请求在get、post等方法返回时自动完成结束请求处理

协程异步

代码语言:javascript
AI代码解释
复制
<span class="hljs-keyword">import</span> tornado.web
<span class="hljs-keyword">import</span> tornado.httpclient


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">IndexHandler</span><span class="hljs-params">(tornado.web.RequestHandler)</span>:</span>
<span class="hljs-meta">    @tornado.gen.coroutine</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get</span><span class="hljs-params">(self)</span>:</span>
        http = tornado.httpclient.AsyncHTTPClient()
        response = <span class="hljs-keyword">yield</span> http.fetch(<span class="hljs-string">"http://www.baidu.com"</span>)
        <span class="hljs-keyword">if</span> response.error:
            self.send_error(<span class="hljs-number">500</span>)
        <span class="hljs-keyword">else</span>:
            print(response.body.decode(<span class="hljs-string">'utf-8'</span>))
            f = open(<span class="hljs-string">'baidu.html'</span>, <span class="hljs-string">'wb'</span>)
            f.write(response.body)
            f.close()
            self.write(<span class="hljs-string">"操作成功"</span>)

也可以将异步Web请求单独出来

代码语言:javascript
AI代码解释
复制
<span class="hljs-keyword">import</span> tornado.web
<span class="hljs-keyword">import</span> tornado.httpclient


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">IndexHandler</span><span class="hljs-params">(tornado.web.RequestHandler)</span>:</span>
<span class="hljs-meta">    @tornado.gen.coroutine</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get</span><span class="hljs-params">(self)</span>:</span>
        rep = <span class="hljs-keyword">yield</span> self.get_body_info(<span class="hljs-string">"http://www.baidu.com"</span>)
        <span class="hljs-keyword">if</span> rep[<span class="hljs-string">'ret'</span>] == <span class="hljs-number">1</span>:
            print(rep[<span class="hljs-string">'body'</span>])
        self.write(<span class="hljs-string">"成功"</span>)

<span class="hljs-meta">    @tornado.gen.coroutine</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_body_info</span><span class="hljs-params">(self, url)</span>:</span>
        rep = {<span class="hljs-string">"ret"</span>:<span class="hljs-number">1</span>}
        http = tornado.httpclient.AsyncHTTPClient()
        response = <span class="hljs-keyword">yield</span> http.fetch(url)
        <span class="hljs-keyword">if</span> response.error:
            rep = {<span class="hljs-string">"ret"</span>:<span class="hljs-number">0</span>}
        <span class="hljs-keyword">else</span>:
            rep[<span class="hljs-string">'body'</span>] = response.body.decode(<span class="hljs-string">'utf-8'</span>)
            f = open(<span class="hljs-string">'baidu.html'</span>, <span class="hljs-string">'wb'</span>)
            f.write(response.body)
            f.close()
            self.write(<span class="hljs-string">"操作成功"</span>)
        <span class="hljs-keyword">raise</span> tornado.gen.Return(rep)  <span class="hljs-comment"># 此处需要注意</span>

代码中我们需要注意的地方是get_body_info返回值的方式,在python 2中,使用了yield的生成器可以使用不返回任何值的return,但不能return value,因此Tornado为我们封装了用于在生成器中返回值的特殊异常tornado.gen.Return,并用raise来返回此返回值

并行协程

Tornado可以同时执行多个异步,并发的异步可以使用列表或字典

代码语言:javascript
AI代码解释
复制
<span class="hljs-keyword">import</span> tornado.web
<span class="hljs-keyword">import</span> tornado.httpclient


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">IndexHandler</span><span class="hljs-params">(tornado.web.RequestHandler)</span>:</span>
<span class="hljs-meta">    @tornado.gen.coroutine</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get</span><span class="hljs-params">(self)</span>:</span>
        self.urls = [<span class="hljs-string">"http://www.baidu.com"</span>,<span class="hljs-string">"http://www.webometrics.info/en/Asia"</span>]
        rep1, rep2 = <span class="hljs-keyword">yield</span> [self.get_url_info(self.urls[<span class="hljs-number">0</span>]), self.get_url_info(self.urls[<span class="hljs-number">1</span>])]
        self.write_response(self.urls[<span class="hljs-number">0</span>], rep1, <span class="hljs-string">'baidu.html'</span>)
        self.write_response(self.urls[<span class="hljs-number">1</span>], rep2, <span class="hljs-string">'Asia.html'</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">write_response</span><span class="hljs-params">(self, url, response, name)</span>:</span>
        self.write(url)
        self.write(<span class="hljs-string">":&lt;br/&gt;"</span>)
        <span class="hljs-keyword">if</span> <span class="hljs-number">1</span> == response[<span class="hljs-string">"ret"</span>]:
            f = open(name, <span class="hljs-string">'wb'</span>)
            f.write(response[<span class="hljs-string">'body'</span>])
            f.close()
            self.write(<span class="hljs-string">"操作成功"</span>)
        <span class="hljs-keyword">else</span>:
            self.write(<span class="hljs-string">"获取数据失败&lt;br/&gt;"</span>)

<span class="hljs-meta">    @tornado.gen.coroutine</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_url_info</span><span class="hljs-params">(self, url)</span>:</span>
        rep = {<span class="hljs-string">"ret"</span>: <span class="hljs-number">1</span>}
        http = tornado.httpclient.AsyncHTTPClient()
        response = <span class="hljs-keyword">yield</span> http.fetch(url)
        <span class="hljs-keyword">if</span> response.error:
            rep = {<span class="hljs-string">"ret"</span>: <span class="hljs-number">0</span>}
        <span class="hljs-keyword">else</span>:
            rep[<span class="hljs-string">'body'</span>] = response.body
        <span class="hljs-keyword">raise</span> tornado.gen.Return(rep)  <span class="hljs-comment"># 此处需要注意</span>

四、关于数据库的异步说明

网站基本都会有数据库操作,而Tornado是单线程的,这意味着如果数据库查询返回过慢,整个服务器响应会被堵塞

数据库查询,实质上也是远程的网络调用;理想情况下,是将这些操作也封装成为异步的;但Tornado对此并没有提供任何支持

这是Tornado的设计,而不是缺陷

一个系统,要满足高流量;是必须解决数据库查询速度问题的!

数据库若存在查询性能问题,整个系统无论如何优化,数据库都会是瓶颈,拖慢整个系统!

异步并不能从本质上提到系统的性能;它仅仅是避免多余的网络响应等待,以及切换线程的CPU耗费。

如果数据库查询响应太慢,需要解决的是数据库的性能问题;而不是调用数据库的前端Web应用

对于实时返回的数据查询,理想情况下需要确保所有数据都在内存中,数据库硬盘IO应该为0;这样的查询才能足够快;而如果数据库查询足够快,那么前端web应用也就无将数据查询封装为异步的必要

就算是使用协程,异步程序对于同步程序始终还是会提高复杂性;需要衡量的是处理这些额外复杂性是否值得

如果后端有查询实在是太慢,无法绕过,Tornaod的建议是将这些查询在后端封装独立封装成为HTTP接口,然后使用Tornado内置的异步HTTP客户端进行调用

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
IO异步
对于耗时的过程,我们将其交给别人(如其另外一个线程)去执行,而我们继续往下处理,当别人执行完耗时操作后再将结果反馈给我们,这就是我们所说的异步
星哥玩云
2022/09/14
6440
cookie操作
set_cookie(name, value, domain=None, expires=None, path=’/’, expires_days=None)
星哥玩云
2022/09/14
6260
cookie操作
高阶应用-celery
用户发起request,并且要等待response返回。但是在视图中有一些耗时的操作,导致用户可能会等待很长时间才能接受response,这样用户体验很差
星哥玩云
2022/09/14
5030
Spring Cloud 微服务实战详细笔记
本文是看某课网关于 SpringCloud 微服务实战的视频总结的笔记,其中涉及了
solocoder
2022/04/06
1K0
Spring Cloud 微服务实战详细笔记
模型继承与对应关系
在一对多关系基础上的父表中使用backref函数,并添加uselist参数来表示一对一关系
星哥玩云
2022/09/14
1.6K0
SpringBoot 企业微信点餐系统实战三:商品信息及买家商品api开发
按照数据库的字段写出对应的实体类 ProductInfo。@Data 是 lombok 的注解,用于自动生成 getter, setter, toString 方法,不懂的可以自行查询相关资料。
solocoder
2022/04/06
7630
SpringBoot 企业微信点餐系统实战三:商品信息及买家商品api开发
视图概述与路由
请求地址会被底层Werkzeug库处理(去除ip和端口),用于进行路由匹配,匹配成功后将找打的视图函数进行调用,处理本次用户的请求
星哥玩云
2022/09/14
3080
增加数据到数据库
一、创建对象 当创建对象时DJango没有操作数据库读写,当调用save()方法时才与数据库进行交互,将对象保存到数据库 __init__()方法已经在models.Model类中进行了重写规定了特殊含义,所以在模型类中不能重写 二、增加方式 对象创建空对象后赋值 u = User() <span class="hljs-comment"># 实例化User模型类</span> u.username = <span class="hljs-string">'lucky'</span> <span clas
星哥玩云
2022/09/14
8040
面向对象-访问限制
使用@property让私有属性可以使用点语法。但本质上还是调用公有方法访问的私有属性,只不过看起来比较美丽
星哥玩云
2022/09/08
1.5K0
相关推荐
IO异步
更多 >
LV.6
这个人很懒,什么都没有留下~
交个朋友
加入腾讯云官网粉丝站
双11活动抢先看 更有社群专属礼券掉落
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档