Python Web框架Tornado的异步处理代码演示样例
1. What is Tornado
Tornado是一个轻量级但高性能的Python web框架,与还有一个流行的Python web框架Django相比。tornado不提供操作数据库的ORM接口及严格的MVC开发模式,但能够提供主要的web server功能。故它是轻量级的;它借助non-blocking and event-driven的I/O模型(epoll或kqueue)实现了一套异步网络库,故它是高性能的。
Tornado的轻量级+高性能特性使得它特别适用于提供web api的场合,使用合理的话,其非堵塞+异步能力能够应对C10K问题。
须要特别注意的是,因为Python的GIL导致多线程总是单核运行的”特点”,tornado处理http请求时,若某个请求的后端响应有堵塞现象(如从DB或磁盘读数据导致处理时间非常长),则会导致其他http请求也被block,这会严重拖累tornado在高并发场景下的性能。
幸运的是。tornado提供了异步处理请求的能力,在异步模式下,我们能够通过传入回调函数或借助tornado提供的tornado.gen.coroutine装饰器,使得tornado内部的io loop在等待当前请求响应结果的同一时候,仍然能够接受其他的http请求,这样就避免了某个耗时操作影响tornado的处理能力。
2. 怎样在tornado框架下编写异步处理代码
Tornado官网文档给出了几个简单的异步代码演示样例,只是说实话,代码太过简单(都是在某个uri的handler类的get或post函数中展现了主要的异步语法),没有多大的实战意义。
在实际项目中。复杂的处理逻辑不可能都堆在get或post函数中,而是会封装在其他class中供handler类的get或post函数调用。
所以,本文给出一个稍复杂的实例,旨在说明怎样在其他class的函数中实现异步处理逻辑,以实现http请求异步化处理的目的。
如果如今的需求是用tornado实现一个web server,支持名为cityhotel的uri方法,当client通过http GET请求訪问该uri时,web server依据query參数指定的城市,去请求存放hotel具体数据的还有一个后端api。进行业务处理后返回某个连锁hotel在该城市的全部门店给client。
如果client GET请求的url格式为:http://host/api/hotel/cityhotel?city=xxx city=xxx">http://hotel_backend/getCityHotels? city=xxx
再如果存放hotel具体数据的后端api接口为:
依据上面的场景,因为我们用tornado实现的web server接到client的请求后,还要去还有一个API接口请求基础数据,而后者在返回前,tornado会block,所以,这样的场景下,tornado最好以异步方式请求那个提供基础数据的API。避免不可控的后端拖累tornado的响应性能。
依据上面描写叙述的业务需求。以下的代码示范了怎样通过异步方式处理业务处理。
模块入口文件(main.py):
#!/bin/env python
import tornado.ioloop
import tornado.web
import tornado.gen
import hotelcore
class CityHotelHandler(tornado.web.RequestHandler):
@tornado.gen.coroutine
def get(self):
## parse query params
params = {}
keys = ['city']
for key in keys:
value = self.get_query_argument(key)
params[key] = value
(status, rsp) = yield hotelcore.HotelApiHandler.get_city_hotel(params['city'])
if 200 == status:
self.set_header('content-type', 'application/json')
self.finish(rsp)
else:
self.set_status(404)
self.finish()
def main():
app_inst = tornado.web.Application([
(r'/api/hotel/cityhotel', CityHotelHandler),
], compress_response = True)
app_inst.listen(8218)
tornado.ioloop.IOLoop.current().start()
if '__main__' == __name__:
main()
处理业务逻辑的module封装在hotelcore.py文件里,代码例如以下:
#!/bin/env python
#-*- encoding: utf-8 -*-
import json
from tornado import gen
from tornado import httpclient
class HotelApiHandler(object):
_cfg_dict = {
'api_host' : 'api.hotelbackend.com',
}
@classmethod
@gen.coroutine
def get_city_hotel(cls, city):
ret = yield cls._parallel_fetch_city_hotel(city)
raise gen.Return((200, ret))
@classmethod
@gen.coroutine
def _parallel_fetch_city_hotel(cls, city):
base_url = 'http://%s/v1/getCityHotel' % (cls._cfg_dict['api_host'])
## hote type: 1=normal room; 2=deluxe room
hotel_type = {'normal': 1, 'deluxe': 2}
urls = []
for v in hotel_type.values():
api_url = '%s?city=%s&level=%s' % (base_url, city, v)
urls.append(api_url)
## issue async http request
http_clt = httpclient.AsyncHTTPClient()
rsps_dict = yield dict(normal_room = http_clt.fetch(urls[0]), deluxe_room = http_clt.fetch(urls[1]))
city_hotel_info = cls._parse_city_hotel(rsps_dict, city)
ret = { }
if len(city_hotel_info):
ret['errno'] = 0
ret['errmsg'] = 'SUCCESS'
ret['data'] = city_hotel_info
else:
ret['errno'] = 1
ret['errmsg'] = 'Service Not Found at This City'
ret['data'] = ''
raise gen.Return(ret)
@classmethod
def _parse_city_hotel(cls, rsp_dict, city):
city_hotel_info = {}
for hotel_level, rsp in rsp_dict.items():
rsp_json = json.loads(rsp.body)
datas = rsp_json['data']
for city_id, city_detail in datas.items():
name = city_detail['name']
if city in name:
city_hotel_info[hotel_level] = city_detail
break
return city_hotel_info
对以上代码的几点补充说明:
- 编写tornado异步处理代码须要对Python的decorator语法和generator/yield语法比較熟悉
- tornado提供的装饰器@gen.coroutine表明被装饰函数是个异步处理函数,该函数的调用不会block tornado主线程
- 被@gen.coroutine装饰的函数中,须要异步运行的耗时函数用yield来调用,yield本身返回的是个generator,结合@gen.coroutine后。它返回一个tornado定义的Future类型的对象
- yield调用的函数在运行过程中。进程控制权会返给主线程,故即使该函数须要较长运行时间,tornado的主线程也能够继续处理其他请求
- 在Python 2.x版本号的语法中。generator中不同意用return返回函数的返回值。必须用tornado提供的raise gen.Return(ret)达到返回的目的。这是个比較tricky的方法
- yield返回的Future对象能够通过调用body属性来获取通过yield调用的函数的返回值
- 仅仅要结合上述几点理解了@gen.coroutine和yield在tornado异步编程中的语法意义,那么,写出复杂的异步调用代码与编写实现同样功能但tornado总体性能无法保证的同步调用代码相比。实现难度就差点儿不存在了。
上面的代码非常多语法细节没有展开,希望实现思路能帮助到有缘人。^_^
參考资料
- Tornado Doc: User’s guide
- Book: Introduction to tornado chapter 5. asynchronous web services
Python Web框架Tornado的异步处理代码演示样例的更多相关文章
- Python Web框架 tornado 异步原理
Python Web框架 tornado 异步原理 参考:http://www.jb51.net/article/64747.htm 待整理
- Python web框架 Tornado异步非阻塞
Python web框架 Tornado异步非阻塞 异步非阻塞 阻塞式:(适用于所有框架,Django,Flask,Tornado,Bottle) 一个请求到来未处理完成,后续一直等待 解决方案: ...
- java 覆盖hashCode()深入探讨 代码演示样例
java 翻盖hashCode()深入探讨 代码演示样例 package org.rui.collection2.hashcode; /** * 覆盖hashcode * 设计HashCode时最重要 ...
- 异步非阻塞IO的Python Web框架--Tornado
Tornado的全称是Torado Web Server,从名字上就可知它可用作Web服务器,但同时它也是一个Python Web的开发框架.最初是在FriendFeed公司的网站上使用,FaceBo ...
- 关于Python Web框架——Tornado
关于Tornado的入门看这篇文章,写的非常好: https://zhuanlan.zhihu.com/p/37382503 Tornado 是一个Python web框架和异步网络库,使用非阻塞网络 ...
- Python web框架——Tornado
Tornado是一个Python Web框架和异步网络库,最初由FriendFeed开发.通过使用非阻塞网络I / O,Tornado可以扩展到数万个开放连接,使其成为需要长时间连接每个用户的长轮询, ...
- Swift语言 简明基础 代码演示样例
开发环境: Mac.Xcode6.0 下面内容均可创建ios common line项目来測试 1.Hello World演示样例 使用xcode创建新的common line项目,查看主文件main ...
- Python web框架 Tornado(二)异步非阻塞
异步非阻塞 阻塞式:(适用于所有框架,Django,Flask,Tornado,Bottle) 一个请求到来未处理完成,后续一直等待 解决方案:多线程,多进程 异步非阻塞(存在IO请求): Torna ...
- Python web框架 Tornado(三)自定义session组件
我们在学习Django框架的过程中,内部封装了session组件,以方便于我们使用进行验证.但是Tornado框架是没有session的,所以如果想使用session的话,就需要我们自己定制相对应的组 ...
随机推荐
- 索引-mysql索引创建、查看、删除及使用示例
mysql索引创建.查看.删除及使用示例 1.创建索引: ALTER TABLE用来创建普通索引.UNIQUE索引或PRIMARY KEY索引. ALTER TABLE table_name ADD ...
- 认识javascript的引擎之--1
前言: 一:每一款浏览器里面都能执行js脚本,那是因为制造商在浏览器里面加入了js引擎.也就是说js引擎在浏览器里面占有一席之地. 1.开始的时候js处于沉睡状态,直到运行页面遇到 <scrip ...
- 515Nod 1126 求递推序列的第n项【矩阵快速幂】
有一个序列是这样定义的:f(1) = 1, f(2) = 1, f(n) = (A * f(n - 1) + B * f(n - 2)) mod 7. 给出A,B和N,求f(n)的值. Input 输 ...
- MNIST手写数字数据集
下载python源代码之后,使用: import input_data mnist = input_data.read_data_sets('MNIST_data/',one_hot=True) 下载 ...
- [HAOI2007]理想的正方形 单调队列 暴力
Code: #include<cstdio> #include<queue> #include<algorithm> using namespace std; #d ...
- bzoj1604 牛的邻居 STL
Description 了解奶牛们的人都知道,奶牛喜欢成群结队.观察约翰的N(1≤N≤100000)只奶牛,你会发现她们已经结成了几个“群”.每只奶牛在吃草的时候有一个独一无二的位置坐标Xi,Yi(l ...
- LightOJ-1341 Aladdin and the Flying Carpet 分解质因数(注意对大素数的优化)
题目链接:https://cn.vjudge.net/problem/LightOJ-1341 题意 给出一个长方形的面积a 让你算整数边长的可能取值,并且两个边都大于给定数字b 思路 唯一分解定理: ...
- python_webApp
提高开发效率:当更改代码后,不重启服务器就能使用新效果 参考链接:https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df ...
- 最长回文字串 (The longest palindrome substring)
这两天去学了一下,觉得下面那篇文章写的很好,有例子,比较容易懂,所以转一下. 以下内容来自:hihoCoder: 小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互 ...
- 【Henu ACM Round#20 F】 Arthur and Brackets
[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 所给的li,ri是左括号从左到右的顺序给的. (且注意长度是2*n 现在我们先把第一个左括号放在第1个位置. 然后考虑第二个位置. ...