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.返回数据格式:

  1. {"ret":1,"start":-1,"end":-1,"country":"\u4e2d\u56fd","province":"\u5317\u4eac","city":"\u5317\u4eac","district":"","isp":"","type":"","desc":""}

3. 回调异步

  1. #!/usr/bin/env python3
  2. # -*- coding:utf-8 -*-
  3. # @Time: 2020/3/15 19:38
  4. # @Author:zhangmingda
  5. # @File: asyncHTTPClient_on_server.py
  6. # @Software: PyCharm
  7. # Description: 学习异步客户端httpclient.AsyncHTTPClient
  8.  
  9. from tornado.options import options
  10. from tornado.options import parse_command_line
  11. from tornado.web import RequestHandler, asynchronous,Application
  12. from tornado.httpserver import HTTPServer
  13. from tornado.ioloop import IOLoop
  14. from tornado.httpclient import AsyncHTTPClient
  15. import tornado
  16. import base64,uuid,os,json
  17.  
  18. class AsyncHttpclientIndexHandler(RequestHandler):
  19. '''学习异步请求客户端'''
  20. #不关闭连接,也不发送响应
  21. @asynchronous
  22. def get(self, *args, **kwargs):
  23. http = AsyncHTTPClient()
  24. req_url = "http://myip.ksyun.com"
  25. print('发起请求:',req_url)
  26. http.fetch(request=req_url,
  27. callback=self.on_response
  28. )
  29. def on_response(self,response):
  30. if response.error:
  31. print('send_error:500')
  32. self.send_error(500)
  33. else:
  34. self.xsrf_token
  35. data = response.body
  36. print('获取返回值response.body : ',data)
  37. self.write(data.decode('utf-8'))
  38. # 发送响应信息,结束请求处理
  39. self.finish()
  40. if __name__ == '__main__':
  41. parse_command_line()
  42. options.define(name='port',default=80,type=int,help="服务监听端口")
  43. url = [
  44. (r'/',AsyncHttpclientIndexHandler)
  45. ]
  46. BASEDIR = os.path.dirname(__file__)
  47. static_path = os.path.join(BASEDIR,'statics')
  48. template_path = os.path.join(BASEDIR,'templates')
  49. app = Application(url,
  50. static_path=static_path,
  51. template_path=template_path,
  52. cookie_secret=base64.b64encode(uuid.uuid4().bytes + uuid.uuid4().bytes),
  53. xsrf_cookies=True,
  54. debug=True
  55. )
  56. http_server = HTTPServer(app)
  57. http_server.listen(options.port)
  58. IOLoop.current().start()

@tornado.web.asynchronous

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

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

不带此装饰器的请求在get、post等方法返回时自动完成结束请求处理(只返回状态码,来不及返回任何数据)

4. 协程异步

在上一节中我们自己封装的装饰器get_coroutine在Tornado中对应的是@tornado.gen.coroutine 装饰器。

  1. class GenAsyncIndexHandler(RequestHandler):
  2. '''使用yield类似协程的方式'''
  3. @coroutine
  4. def get(self, *args, **kwargs):
  5. req_url = "http://myip.ksyun.com"
  6. http = AsyncHTTPClient()
  7. response = yield http.fetch(req_url)
  8. if response.error:
  9. self.send_error(500)
  10. else:
  11. self.write(response.body)

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

  1. class Gen2AsyncIndexHandler(RequestHandler):
  2. '''使用yield类似协程的方式分成两个函数'''
  3. @coroutine
  4. def get(self, *args, **kwargs):
  5. req_url = "http://myip.ksyun.com"
  6. rep = yield self.get_client_ip(req_url)
  7. print(type(rep))
  8. if rep != 'error':
  9. self.write(rep)
  10. else:
  11. self.send_error(500)
  12. @coroutine
  13. def get_client_ip(self,url):
  14. http = AsyncHTTPClient()
  15. response = yield http.fetch(url)
  16. if response.error:
  17. rep = 'error'
  18. else:
  19. rep = response.body
  20. raise tornado.gen.Return(rep)

代码中我们需要注意的地方是get_ip_info返回值的方式,在python 2中,使用了yield的生成器可以使用不返回任何值的return,但不能return value,

因此Tornado为我们封装了用于在生成器中返回值的特殊异常tornado.gen.Return,并用raise来返回此返回值。

并行协程

Tornado可以同时执行多个异步,并发的异步可以使用列表如下:

  1. class QueryIpInfoHandler(tornado.web.RequestHandler):
  2. '''测试多个请求并发协程客户端'''
  3. @coroutine
  4. def get(self):
  5. ips = self.get_query_arguments('ip')
  6. if len(ips) > 0:
  7. for ip in ips:
  8. ret = yield self.get_ip_info(ip)
  9. self.write_response(ip,ret)
  10. else:
  11. client_ip = self.request.remote_ip
  12. print(client_ip)
  13. ret = yield self.get_ip_info(client_ip)
  14. print(ret)
  15. self.write_response(client_ip,ret)
  16.  
  17. def write_response(self, ip, response):
  18. self.write(ip)
  19. self.write(":<br/>")
  20. if None != response:
  21. self.write("&nbsp&nbsp国家:%s <br/>\
  22. &nbsp&nbsp省份: %s <br/>\
  23. &nbsp&nbsp城市: %s <br/>\
  24. &nbsp&nbsp单位: %s <br/>\
  25. &nbsp&nbsp运营商: %s <br/>" % (response[0],
  26. response[1],
  27. response[2],
  28. response[3] if response[3] != '' else "未知",
  29. response[4],))
  30. else:
  31. self.write("查询IP信息错误<br/>")
  32.  
  33. @tornado.gen.coroutine
  34. def get_ip_info(self, ip):
  35. http = tornado.httpclient.AsyncHTTPClient()
  36. response = yield http.fetch("http://freeapi.ipip.net/" + ip)
  37. if response.error:
  38. rep = None
  39. else:
  40. rep = json.loads(response.body)
  41. raise tornado.gen.Return(rep)

5. 关于数据库的异步说明

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

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

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

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

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

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

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

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

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

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

7.2 Tornado异步的更多相关文章

  1. tornado异步请求的理解(转)

    tornado异步请求的理解 http://www.kankanews.com/ICkengine/archives/88953.shtml 官网第一段话: Tornado is a Python w ...

  2. 使用Tornado异步接入第三方(支付宝)支付

    目前国内比较流行的第三方支付主要有支付宝和微信支付,博主最近研究了下如何用Python接入支付宝支付,这里我以Tornado作为web框架,接入支付宝构造支付接口. 使用Tornado异步接入支付宝支 ...

  3. Tornado异步非阻塞的使用以及原理

    Tornado 和现在的主流 Web 服务器框架(包括大多数 Python 的框架)有着明显的区别:它是非阻塞式服务器,而且速度相当快.得利于其 非阻塞的方式和对 epoll 的运用,Tornado ...

  4. Python Web框架 tornado 异步原理

    Python Web框架 tornado 异步原理 参考:http://www.jb51.net/article/64747.htm 待整理

  5. Tornado异步(2)

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

  6. tornado异步(1)

    1. 同步 我们用两个函数来模拟两个客户端请求,并依次进行处理: # coding:utf-8 def req_a(): """模拟请求a""&quo ...

  7. tornado异步请求响应速度的实例测试

    tornado异步请求响应速度的实例测试

  8. 5.(基础)tornado异步

    终于到了传说中的异步了,感觉异步这个名字听起来就很酷酷的,以前还不是多擅长Python时,就跑去看twisted的源码,结果给我幼小的心灵留下了创伤.反正包括我在内,都知道异步编程很强大,但是却很少在 ...

  9. Python web框架 Tornado异步非阻塞

    Python web框架 Tornado异步非阻塞   异步非阻塞 阻塞式:(适用于所有框架,Django,Flask,Tornado,Bottle) 一个请求到来未处理完成,后续一直等待 解决方案: ...

随机推荐

  1. DataGrid首次进入页面时,不加载任何数据[转]

    首次不加载数据问题,必须搞明白如何才能不加载数据.根据Easu UI的官方API: http://www.jeasyui.com/documentation/ 仔细观察DataGrid的事件当中有一个 ...

  2. Python之99乘法表代码

    #coding=utf-8 #左下三角格式输出九九乘法表 for i in range(1,10):      for j in range(1,i+1):          print " ...

  3. Abp Vnext Blazor替换UI组件 集成BootstrapBlazor(详细过程)

    Abp Vnext自带的blazor项目使用的是 Blazorise,但是试用后发现不支持多标签.于是想替换为BootstrapBlazor. 过程比较复杂,本人已经把模块写好了只需要替换掉即可. 点 ...

  4. Jmeter BlazeMeter实现web录制

      1. BlazeMeter安装和注册 BlazeMeter是一款与Apache JMeter兼容的chrome插件,采用BlazeMeter可以方便的进行流量录制和脚本生成,作为接口测试脚本编写的 ...

  5. FJOI2020 的两道组合计数题

    最近细品了 FJOI2020 的两道计数题,感觉抛开数据范围不清还卡常不谈里面的组合计数技巧还是挺不错的.由于这两道题都基于卡特兰数的拓展,所以我们把它们一并研究掉. 首先是 D1T3 ,先给出简要题 ...

  6. Codeforces 1109F - Sasha and Algorithm of Silence's Sounds(LCT)

    Codeforces 题面传送门 & 洛谷题面传送门 讲个笑话,这题是 2020.10.13 dxm 讲题时的一道例题,而我刚好在一年后的今天,也就是 2021.10.13 学 LCT 时做到 ...

  7. NFLSOJ #917 -「lych_cys模拟题2018」橘子树(树剖+ODT+莫反统计贡献的思想+动态开点线段树)

    题面传送门 sb 出题人不在题面里写 \(b_i=0\) 导致我挂成零蛋/fn/fn 首先考虑树链剖分将路径问题转化为序列上的问题,因此下文中简称"位置 \(i\)"表示 DFS ...

  8. Shell 变量嵌套

    实现:eval 1 a="indv1" 2 indv1="Sus1" 3 4 eval tmp='$'$a 5 echo $tmp //这里 echo 返回值为 ...

  9. Perl if条件判断

    Perl 条件语句是通过一条或多条语句的执行结果(True或者False)来决定执行的代码块. 条件判断常用: True         #布尔值 not True   #布尔值 ! True    ...

  10. JVM2 类加载子系统

    目录 类加载子系统 类加载器子系统 类加载器ClassLoader角色 类加载的过程 案例 加载Loading 连接Linking 初始化Intialization clinit() 类的加载器 虚拟 ...