7.2 Tornado异步
7.2 Tornado异步
因为epoll主要是用来解决网络IO的并发问题,所以Tornado的异步编程也主要体现在网络IO的异步上,即异步Web请求。
1. 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: 异常(可有可无)
2. 测试接口
新浪IP地址库
接口说明
1.请求接口(GET):
http://int.dpool.sina.com.cn/iplookup/iplookup.php?format=json&ip=[ip地址字串]
2.响应信息:
(json格式的)国家 、省(自治区或直辖市)、市(县)、运营商
3.返回数据格式:
{"ret":1,"start":-1,"end":-1,"country":"\u4e2d\u56fd","province":"\u5317\u4eac","city":"\u5317\u4eac","district":"","isp":"","type":"","desc":""}
3. 回调异步
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# @Time: 2020/3/15 19:38
# @Author:zhangmingda
# @File: asyncHTTPClient_on_server.py
# @Software: PyCharm
# Description: 学习异步客户端httpclient.AsyncHTTPClient from tornado.options import options
from tornado.options import parse_command_line
from tornado.web import RequestHandler, asynchronous,Application
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop
from tornado.httpclient import AsyncHTTPClient
import tornado
import base64,uuid,os,json class AsyncHttpclientIndexHandler(RequestHandler):
'''学习异步请求客户端'''
#不关闭连接,也不发送响应
@asynchronous
def get(self, *args, **kwargs):
http = AsyncHTTPClient()
req_url = "http://myip.ksyun.com"
print('发起请求:',req_url)
http.fetch(request=req_url,
callback=self.on_response
)
def on_response(self,response):
if response.error:
print('send_error:500')
self.send_error(500)
else:
self.xsrf_token
data = response.body
print('获取返回值response.body : ',data)
self.write(data.decode('utf-8'))
# 发送响应信息,结束请求处理
self.finish()
if __name__ == '__main__':
parse_command_line()
options.define(name='port',default=80,type=int,help="服务监听端口")
url = [
(r'/',AsyncHttpclientIndexHandler)
]
BASEDIR = os.path.dirname(__file__)
static_path = os.path.join(BASEDIR,'statics')
template_path = os.path.join(BASEDIR,'templates')
app = Application(url,
static_path=static_path,
template_path=template_path,
cookie_secret=base64.b64encode(uuid.uuid4().bytes + uuid.uuid4().bytes),
xsrf_cookies=True,
debug=True
)
http_server = HTTPServer(app)
http_server.listen(options.port)
IOLoop.current().start()
@tornado.web.asynchronous
此装饰器用于回调形式的异步方法,并且应该仅用于HTTP的方法上(如get、post等)。
此装饰器不会让被装饰的方法变为异步,而只是告诉框架被装饰的方法是异步的,当方法返回时响应尚未完成。只有在request handler调用了finish方法后,才会结束本次请求处理,发送响应。
不带此装饰器的请求在get、post等方法返回时自动完成结束请求处理(只返回状态码,来不及返回任何数据)。
4. 协程异步
在上一节中我们自己封装的装饰器get_coroutine在Tornado中对应的是@tornado.gen.coroutine 装饰器。
class GenAsyncIndexHandler(RequestHandler):
'''使用yield类似协程的方式'''
@coroutine
def get(self, *args, **kwargs):
req_url = "http://myip.ksyun.com"
http = AsyncHTTPClient()
response = yield http.fetch(req_url)
if response.error:
self.send_error(500)
else:
self.write(response.body)
也可以将异步Web请求单独出来:
class Gen2AsyncIndexHandler(RequestHandler):
'''使用yield类似协程的方式分成两个函数'''
@coroutine
def get(self, *args, **kwargs):
req_url = "http://myip.ksyun.com"
rep = yield self.get_client_ip(req_url)
print(type(rep))
if rep != 'error':
self.write(rep)
else:
self.send_error(500)
@coroutine
def get_client_ip(self,url):
http = AsyncHTTPClient()
response = yield http.fetch(url)
if response.error:
rep = 'error'
else:
rep = response.body
raise tornado.gen.Return(rep)
代码中我们需要注意的地方是get_ip_info返回值的方式,在python 2中,使用了yield的生成器可以使用不返回任何值的return,但不能return value,
因此Tornado为我们封装了用于在生成器中返回值的特殊异常tornado.gen.Return,并用raise来返回此返回值。
并行协程
Tornado可以同时执行多个异步,并发的异步可以使用列表如下:
class QueryIpInfoHandler(tornado.web.RequestHandler):
'''测试多个请求并发协程客户端'''
@coroutine
def get(self):
ips = self.get_query_arguments('ip')
if len(ips) > 0:
for ip in ips:
ret = yield self.get_ip_info(ip)
self.write_response(ip,ret)
else:
client_ip = self.request.remote_ip
print(client_ip)
ret = yield self.get_ip_info(client_ip)
print(ret)
self.write_response(client_ip,ret) def write_response(self, ip, response):
self.write(ip)
self.write(":<br/>")
if None != response:
self.write("  国家:%s <br/>\
  省份: %s <br/>\
  城市: %s <br/>\
  单位: %s <br/>\
  运营商: %s <br/>" % (response[0],
response[1],
response[2],
response[3] if response[3] != '' else "未知",
response[4],))
else:
self.write("查询IP信息错误<br/>") @tornado.gen.coroutine
def get_ip_info(self, ip):
http = tornado.httpclient.AsyncHTTPClient()
response = yield http.fetch("http://freeapi.ipip.net/" + ip)
if response.error:
rep = None
else:
rep = json.loads(response.body)
raise tornado.gen.Return(rep)
5. 关于数据库的异步说明
网站基本都会有数据库操作,而Tornado是单线程的,这意味着如果数据库查询返回过慢,整个服务器响应会被堵塞。
数据库查询,实质上也是远程的网络调用;理想情况下,是将这些操作也封装成为异步的;但Tornado对此并没有提供任何支持。
这是Tornado的设计,而不是缺陷。
一个系统,要满足高流量;是必须解决数据库查询速度问题的!
数据库若存在查询性能问题,整个系统无论如何优化,数据库都会是瓶颈,拖慢整个系统!
异步并不能从本质上提到系统的性能;它仅仅是避免多余的网络响应等待,以及切换线程的CPU耗费。
如果数据库查询响应太慢,需要解决的是数据库的性能问题;而不是调用数据库的前端Web应用。
对于实时返回的数据查询,理想情况下需要确保所有数据都在内存中,数据库硬盘IO应该为0;这样的查询才能足够快;而如果数据库查询足够快,那么前端web应用也就无将数据查询封装为异步的必要。
就算是使用协程,异步程序对于同步程序始终还是会提高复杂性;需要衡量的是处理这些额外复杂性是否值得。
如果后端有查询实在是太慢,无法绕过,Tornaod的建议是将这些查询在后端封装独立封装成为HTTP接口,然后使用Tornado内置的异步HTTP客户端进行调用。
7.2 Tornado异步的更多相关文章
- tornado异步请求的理解(转)
tornado异步请求的理解 http://www.kankanews.com/ICkengine/archives/88953.shtml 官网第一段话: Tornado is a Python w ...
- 使用Tornado异步接入第三方(支付宝)支付
目前国内比较流行的第三方支付主要有支付宝和微信支付,博主最近研究了下如何用Python接入支付宝支付,这里我以Tornado作为web框架,接入支付宝构造支付接口. 使用Tornado异步接入支付宝支 ...
- Tornado异步非阻塞的使用以及原理
Tornado 和现在的主流 Web 服务器框架(包括大多数 Python 的框架)有着明显的区别:它是非阻塞式服务器,而且速度相当快.得利于其 非阻塞的方式和对 epoll 的运用,Tornado ...
- Python Web框架 tornado 异步原理
Python Web框架 tornado 异步原理 参考:http://www.jb51.net/article/64747.htm 待整理
- Tornado异步(2)
Tornado异步 因为epoll主要是用来解决网络IO的并发问题,所以Tornado的异步编程也主要体现在网络IO的异步上,即异步Web请求. 1. tornado.httpclient.Async ...
- tornado异步(1)
1. 同步 我们用两个函数来模拟两个客户端请求,并依次进行处理: # coding:utf-8 def req_a(): """模拟请求a""&quo ...
- tornado异步请求响应速度的实例测试
tornado异步请求响应速度的实例测试
- 5.(基础)tornado异步
终于到了传说中的异步了,感觉异步这个名字听起来就很酷酷的,以前还不是多擅长Python时,就跑去看twisted的源码,结果给我幼小的心灵留下了创伤.反正包括我在内,都知道异步编程很强大,但是却很少在 ...
- Python web框架 Tornado异步非阻塞
Python web框架 Tornado异步非阻塞 异步非阻塞 阻塞式:(适用于所有框架,Django,Flask,Tornado,Bottle) 一个请求到来未处理完成,后续一直等待 解决方案: ...
随机推荐
- 在Ubuntu上安装Docker Engine
在Ubuntu上安装Docker Engine 这篇文章是介绍如何在在Ubuntu上安装Docker Engine,就是Google翻译官方文档的版本,英语好的直接官方原文.原文 要在Ubuntu上开 ...
- 彻底搞清楚 JavaScript 的原型和原型链
JavaScript真的挺无语的,怪不得看了那么多的介绍文章还是一头雾水,直到自己终于弄懂了一点点之后才深有体会: 先从整体说起吧,发现没有基础做依据,那都是空中楼阁: 先从基础开始介绍吧,又发现基础 ...
- 洛谷 P4062 - [Code+#1]Yazid 的新生舞会(权值线段树)
题面传送门 题意: 给出一个序列 \(a\),求 \(a\) 有多少个子区间 \([l,r]\),满足这个区间中出现次数最多的数出现次数 \(>\dfrac{r-l+1}{2}\) \(1 \l ...
- 洛谷 P5206 - [WC2019]数树(集合反演+NTT)
洛谷题面传送门 神仙多项式+组合数学题,不过还是被我自己想出来了( 首先对于两棵树 \(E_1,E_2\) 而言,为它们填上 \(1\sim y\) 使其合法的方案数显然是 \(y\) 的 \(E_1 ...
- seq生成格式化字符
[root@ansz.quan.bbs ~]$seq -s "+" 10 1+2+3+4+5+6+7+8+9+10 seq为生成数字序列 -s 分隔符
- Yii2 源码分析 入口文件执行流程
Yii2 源码分析 入口文件执行流程 1. 入口文件:web/index.php,第12行.(new yii\web\Application($config)->run()) 入口文件主要做4 ...
- Excel—分组然后取每组中对应时间列值最大的或者最小的
1.MAX(IF(A:A=D2,B:B)) 输入函数公式后,按Ctrl+Shift+Enter键使函数公式成为数组函数公式. Ctrl+Shift+Enter: 按住Ctrl键不放,继续按Shift键 ...
- js浮点运算的坑
1,js浮点型小数点运算的问题. 这么简单的计算,js竟然算的是错的,究其原因,是因为js小数在内存存储方式的原因. 具体原因: JavaScript 里的数字是采用 IEEE 754 标准的 64 ...
- 面向多场景而设计的 Erda Pipeline
作者|林俊(万念) 来源|尔达 Erda 公众号 Erda Pipeline 是端点自研.用 Go 编写的一款企业级流水线服务.截至目前,已经为众多行业头部客户提供交付和稳定的服务. 为什么我们坚持自 ...
- ace
ace An ace is a playing card, die or domino with a single pip. In the standard French deck, an ace h ...