tornado异步非阻塞请求
2019-04-09 本文已影响0人
来一碗花甲粉
基础知识
首先对同步/异步、阻塞/非阻塞、以及tornado中asyncio进行了解,参考下面的文章
1.python中的异步实践与tornado应用
https://my.oschina.net/yangyanxing/blog/3001436
2.关于 asyncio
https://binglau7.github.io/2018/01/30/%E5%85%B3%E4%BA%8E-asyncio-%E7%9A%84%E5%96%83%E5%96%83%E8%87%AA%E8%AF%AD/
注意
程序中的数据库读取等io操作都要改为异步的,才能真正实现异步性能,否则还是会阻塞请求
实践
- 因为项目中历史问题,让前端统一接口调用方式,在新tornado后端服务中,采用了异步转发
使用
python3.7 中 async/await
tornado 5.1 中 tornado.httpclient.AsyncHTTPClient()
//tornado.gen.Task 方法在5.1版本以后弃用
from .base.base_handler import BaseHandler
import tornado.web
import tornado.gen
import tornado.httpclient
from tornado.options import options
import time
import asyncio
class RedirectHandler(BaseHandler):
async def post(self):
method = self.get_argument('method')
modular = self.get_argument('modular')
headers = self.request.headers
body = self.request.body
http_client = tornado.httpclient.AsyncHTTPClient()
url = 'http://localhost:{0}/{1}/{2}'.format(options.port, modular, method)
resp = await http_client.fetch(
url,
method="POST",
headers=headers,
body=body,
validate_cert=False
)
#响应的headers
#print(resp.headers)
self._headers = resp.headers
self.write(resp.body)
- 多线程使用
项目中需要导出表格,比较耗时,使用异步多线程
使用
python3.7 中 ThreadPoolExecutor
tornado 5.1 +
@tornado.gen.coroutine
@run_on_executor
import tornado.web
import tornado.gen
from tornado.ioloop import IOLoop
from tornado.concurrent import run_on_executor
from concurrent.futures import ThreadPoolExecutor
from .base.base_handler import BaseHandler
from utils.json_encoder import convert2json, error2json
from constants import *
from service.export_deliver.dispatch_center import ExportDeliverDispatch
from utils.time_convert import timestamp_to_datetime
class ExportDeliverHandler(BaseHandler):
executor = ThreadPoolExecutor(10) # 线程池
#@tornado.web.asynchronous
@tornado.gen.coroutine
def post(self):
params = self.request.body
params = json.loads(params)
temp_id = params.get('tempId', '')
dataset_col = params.get('dataset').get('colName')
filename, stream = yield self.deliver(params)
#Content-Type这里我写的时候是固定的了,也可以根据实际情况传值进来
self.set_header('Content-Type', 'application/octet-stream')
# 中文文件名用quote解决
self.set_header('Content-Disposition', "attachment; filename={0}".format(quote(filename)))
#读取的模式需要根据实际情况进行修改
self.write(stream)
@run_on_executor
def deliver(self, params):
# dosomething
return filename, stream
可用遇到报错信息:
RuntimeError: There is no current event loop in thread ThreadPoolExecutor-xxx
解决:
线程中执行代码的开始,加上下面代码(在2.关于 asyncio中也有介绍)
new_loop = asyncio.new_event_loop()
asyncio.set_event_loop(new_loop)