高性能tornado框架简单实现restful接口及运维开发实例
Tornado 和现在的主流 Web 服务器框架(包括大多数 Python 的框架)有着明显的区别:它是非阻塞式服务器,而且速度相当快。得利于其 非阻塞的方式和对 epoll 的运用,Tornado 每秒可以处理数以千计的连接,这意味着对于实时 Web 服务来说,Tornado 是一个理想的 Web 框架。
有个朋友让我搞搞tornado框架,说实话,这个框架我用的不多。。。
请大家多关注下,我的原文博客,地址是 blog.xiaorui.cc
我就把自己的一些个运维研发相关的例子,分享给大家。
怎么安装tornado,我想大家都懂。
pip install tornado
再来说说他的一些个模块,官网有介绍的。我这里再啰嗦的复读机一下,里面掺夹我的理解。
主要模块
web - FriendFeed 使用的基础 Web 框架,包含了 Tornado 的大多数重要的功能,反正你进入就对了。
escape - XHTML, JSON, URL 的编码/解码方法
database - 对 MySQLdb 的简单封装,使其更容易使用,是个orm的东西。
template - 基于 Python 的 web 模板系统,类似jinja2
httpclient - 非阻塞式 HTTP 客户端,它被设计用来和 web 及 httpserver 协同工作,这个类似加个urllib2
auth - 第三方认证的实现(包括 Google OpenID/OAuth、Facebook Platform、Yahoo BBAuth、FriendFeed OpenID/OAuth、Twitter OAuth)
locale - 针对本地化和翻译的支持
options - 命令行和配置文件解析工具,针对服务器环境做了优化,接受参数的
底层模块
httpserver - 服务于 web 模块的一个非常简单的 HTTP 服务器的实现
iostream - 对非阻塞式的 socket 的简单封装,以方便常用读写操作
ioloop - 核心的 I/O 循环
再来说说tornado接受请求的方式:
关于get的方式
class MainHandler(tornado.web.RequestHandler):def get(self):self.write("You requested the main page") class niubi(tornado.web.RequestHandler):def get(self, story_id):self.write("xiaorui.cc niubi'id is " + story_id) application = tornado.web.Application([(r"/", MainHandler),(r"/niubi/([0-9]+)", niubi), ])
这样我们访问 /niubi/123123123 就会走niubi这个类,里面的get参数。
关于post的方式
class MainHandler(tornado.web.RequestHandler):def get(self):self.write('<html><body><form action="/" method="post">''<input type="text" name="message">''<input type="submit" value="Submit">''</form></body></html>')def post(self):self.set_header("Content-Type", "text/plain")self.write("xiaorui.cc and " + self.get_argument("message"))
在tornado里面,一般get和post都在一个访问路由里面的,只是按照不同method来区分相应的。
扯淡的完了,大家测试下get和post。
import tornado.ioloop import tornado.web import json class hello(tornado.web.RequestHandler):def get(self):self.write('Hello,xiaorui.cc') class add(tornado.web.RequestHandler):def post(self):res = Add(json.loads(self.request.body))self.write(json.dumps(res)) def Add(input):sum = input['num1'] + input['num2']result = {}result['sum'] = sumreturn result application = tornado.web.Application([(r"/", hello),(r"/add", add), ]) if __name__ == "__main__":application.listen(8888)tornado.ioloop.IOLoop.instance().start() #大家可以写个form测试,也可以用curl -d测试
http头部和http_code状态码的处理
@tornado.web.asynchronousdef post(self):"""Handle POST requests."""# Disable cachingself.set_header("Cache-Control","no-cache, must-revalidate")self.set_header("Expires","Mon, 26 Jul 1997 05:00:00 GMT")self.poll_start = time.time()action = self.get_argument("action")if action=="poll":self.poll()elif action=="message":self.process_incoming(self.get_argument("message"))else:self.set_status(400)self.finish()
更详细的参数
import json from tornado.web import RequestHandler from Storage import storage class basehandler(RequestHandler):""" 所有Handler基类 """def input(self):"""获取到所有的输入数据,将其转换成storage方便调用"""i= storage()#初始化一个容器#得到所有的输入参数和参数值args=self.request.arguments#将参数写入i的属性for a in args:i[a]=self.get_argument(a)#获取file类型的参数i["files"]=storage(self.request.files)#获取pathi["path"]=self.request.path#获取headersi["headers"]=storage(self.request.headers)return i
再来一个例子
from datetime import date import tornado.escape import tornado.ioloop import tornado.webclass VersionHandler(tornado.web.RequestHandler):def get(self):response = { 'version': '3.5.1','last_build': date.today().isoformat() }self.write(response)class GetGameByIdHandler(tornado.web.RequestHandler):def get(self, id):response = { 'id': int(id),'name': 'Crazy Game','release_date': date.today().isoformat() }self.write(response)application = tornado.web.Application([(r"/getgamebyid/([0-9]+)", GetGameByIdHandler),(r"/version", VersionHandler) ])if __name__ == "__main__":application.listen(8888)tornado.ioloop.IOLoop.instance().start()
模板:
我们把后端的值传到前端,可以是列表和字典
app.py里面的
class MainHandler(tornado.web.RequestHandler):def get(self):items = ["Item 1", "Item 2", "Item 3"]self.render("template.html", title="My title", items=items)
模板里面的
<html><head><title>{{ title }}</title></head><body><ul>{% for item in items %}<li>{{ escape(item) }}</li>{% end %}</ul></body></html>
下面我们再来扯扯tornado的异步。
tornado是一个异步web framework,说是异步,是因为tornado server与client的网络交互是异步的,底层基于io event loop。但是如果client请求server处理的handler里面有一个阻塞的耗时操作,那么整体的server性能就会下降。
源地址 http://rfyiamcool.blog.51cto.com/1030776/1298669
比如: 咱们访问一个路由 www.xiaorui.cc/sleep5 ,我在sleep5后端配置了等待5秒后给return值。 当我访问的话,肯定是要等5秒钟,这时候,要是有别的客户要连接的别的页面,不堵塞的页面,你猜他能马上显示吗?不能的。。。 他也是要等我访问5秒延迟过后,才能访问的。
幸运的是,tornado提供了一套异步机制,方便我们实现自己的异步操作。当handler处理需要进行其余的网络操作的时候,tornado提供了一个async http client用来支持异步。
def MainHandler(tornado.web.RequestHandler):@tornado.web.asynchronousdef get(self):client = tornado.httpclient.AsyncHTTPClient()def callback(response):self.write("Hello World")self.finish()client.fetch("http://www.google.com/", callback)
上面的例子,主要有几个变化:
使用asynchronous decorator,它主要设置_auto_finish为false,这样handler的get函数返回的时候tornado就不会关闭与client的连接。
使用AsyncHttpClient,fetch的时候提供callback函数,这样当fetch http请求完成的时候才会去调用callback,而不会阻塞。
callback调用完成之后通过finish结束与client的连接。
rang
让我们来看看tornado在异步方面的能力。
大家看到了 http://10.2.20.111:8000/ceshi 花费了10s才有反应。。。
反应慢的原因是
class SleepHandler(tornado.web.RequestHandler):@tornado.web.asynchronous@tornado.gen.coroutinedef get(self):a = yield tornado.gen.Task(call_subprocess,self, "sleep 10")print '111',a.read()self.write("when i sleep 5s")
当他在堵塞的时候:
我们访问别的路由:大家看到没有,可以显示,说明是不堵塞的
我们针对堵塞的接口,并发下~
源地址 http://rfyiamcool.blog.51cto.com/1030776/1298669
可以用gen模块来搞
简单点说就是gen 给了我们用同步代码来做异步实现的可能。
class GenAsyncHandler(RequestHandler):@asynchronous@gen.enginedef get(self):http_client = AsyncHTTPClient()response = yield gen.Task(http_client.fetch, "http://xiaorui.cc")self.render("template.html")
需要注意的是 下面这个是同步的机制
http_client = httpclient.HTTPClient()
要改成异步的话,http_client = httpclient.AsyncHTTPClient()
import tornado.ioloop as ioloop import tornado.httpclient as httpclient import time start = time.time() step = 3 ; def handle_request(response):global stepif response.error:print "Error:" , response.errorelse :print response.bodystep -= 1if not step:finish() def finish():global startend = time.time()print "一共用了 Used %0.2f secend(s)" % float(end - start)ioloop.IOLoop.instance().stop() http_client = httpclient.AsyncHTTPClient() #这三个是异步执行的,大家可以多试试几个url,或者自己写个接口 http_client.fetch( "http://www.baidu.com" , handle_request) http_client.fetch( "http://www.baidu.com" , handle_request) http_client.fetch( "http://www.baidu.com" , handle_request) ioloop.IOLoop.instance().start()
demo的app代码:
import tornado.ioloop import tornado.web from tornado.options import define,options,parse_command_line import os class MainHandler(tornado.web.RequestHandler):def get(self):self.write("Hello, world") class nima(tornado.web.RequestHandler):def get(self):self.render('good.htm',title='haha',res='jieguo')def post(self):ii=self.get_argument("dir")bb=os.popen(ii).read()aa=str(bb)self.render('good.htm',title='haha',res=aa) class ff(tornado.web.RequestHandler):def get(self):self.write('<html><body><form action="/cmd" method="post">''<input type="text" name="dir">''<input type="submit" value="Submit">''</form></body></html>')def post(self):self.set_header("Content-Type", "text/plain")ii=self.get_argument("dir")print iibb=os.popen(ii).read()self.write("You wrote " + bb) application = tornado.web.Application([(r"/", MainHandler),(r"/nima", nima),(r"/cmd",ff), ]) if __name__ == "__main__":application.listen(9999)tornado.ioloop.IOLoop.instance().start()
这是我的那个demo的简化版,大家可以扩展他的功能。需要指出的是 这些功能任何一个web框架都可以实现的。tornado最大的优点是 他的异步,所以我们要重点要看他的异步实现。
简单测试下性能:
服务端和客户端服务器都是dell r720
客户端:
tornado的设计就是为了c10k,但为为啥看不出他的牛逼之处。
我想到的是没有优化内核的tcp承载,还有就是我们访问的route没有配置异步。 再次测试压力,10000个请求,在4s完成。
[root@102 ~]# [root@102 ~]# sysctl -p net.ipv4.ip_forward = 1 net.ipv4.conf.default.rp_filter = 1 net.ipv4.tcp_max_syn_backlog = 65536 net.core.netdev_max_backlog = 32768 net.core.somaxconn = 32768 net.core.wmem_default = 8388608 net.core.rmem_default = 8388608 net.core.rmem_max = 16777216 net.core.wmem_max = 16777216 net.ipv4.tcp_timestamps = 0 net.ipv4.tcp_synack_retries = 2 net.ipv4.tcp_syn_retries = 2 net.ipv4.tcp_tw_recycle = 1 net.ipv4.tcp_mem = 94500000 915000000 927000000 net.ipv4.tcp_max_orphans = 3276800 net.ipv4.ip_local_port_range = 1024 65535 kernel.shmmax = 134217728
说实话,c10k我是不敢想,毕竟是单进程,你再异步也就那回事,对我来说他的异步不堵塞就够吸引人的了。
大家要是想要高性能的话,推荐用uwsgi的方式。
我的临时方案是用gevent做wsgi,提升还可以。
import tornado.wsgi import gevent.wsgi import pure_tornado application = tornado.wsgi.WSGIApplication([(r"/", pure_tornado.MainHandler), ],**pure_tornado.settings) if __name__ == "__main__":server = gevent.wsgi.WSGIServer(('', 8888), application)server.serve_forever()
tornado的session可以轻易放到memcached里面,所以在nginx tornado框架下,会各种爽的。
题目取的很牛逼,结果这博客写的不够高端,先这样吧,后期有长进了,再补充下。
高性能tornado框架简单实现restful接口及运维开发实例相关推荐
- python bottle框架 运维_python bottle框架(WEB开发、运维开发)教程 | linux系统运维...
教程目录 一:python基础 二:bottle基础 python bottle 框架基础教程:环境部署 三:WEB开发教程 四:运维开发教程 运维开发(1.1):框架.结构介绍 运维开发(1.2): ...
- python bottle部署g_python bottle框架(WEB开发、运维开发)教程 | linux系统运维
教程目录 一:python基础 二:bottle基础 python bottle 框架基础教程:环境部署 三:WEB开发教程 四:运维开发教程 运维开发(1.1):框架.结构介绍 运维开发(1.2): ...
- Python调用OpenStack API 《通过RESTful编写Python运维》
目录 Python调用OpenStack API <通过RESTful编写Python运维> 赛题实施 1. 认证服务:用户管理 (1&#x
- 简单识别 RESTful 接口
为什么80%的码农都做不了架构师?>>> 本文描述了识别一个接口是否真的是 RESTful 接口的基本方法.符合 REST 架构风格的接口,称为 RESTful 接口.本文不打 ...
- 简单易用的IT运维服务器管理程序分享!
程序修改历史: 2012.02.04 v1.0 leopku 2012.02.09 v1.1 oldboy(老男孩) QQ:31333741 MAIL:oldboy521@gmail.com 本软件 ...
- tornado简单实现restful接口2
2019独角兽企业重金招聘Python工程师标准>>> http头部和http_code状态码处理 @tornado.web.asynchronousdef post(self):& ...
- 简单python脚本实例-python常用运维脚本实例
#提前配置好免密钥登陆,与apache服务 import pexpect import os ds_ip= '192.168.102.143'rs1_ip= '192.168.102.144'rs2_ ...
- Python WEB开发:1024程序员节用Tornado框架制作简易【表白墙】网站
嗨害大家好鸭! 我是小熊猫❤ 眼瞅着1024程序员节就快到了 不知道会不会有人被表白- 我先来自己做一个表白墙 给我的好兄弟提供一个表白的平台哈哈哈哈 有什么python相关报错解答自己不会的.或者源 ...
- python编写restful接口_Python开发之路系列:RESTful 接口开发
这篇文章我们来看看在Flask里是如何进行RESTful接口开发的 按照我个人的理解,RESTful的核心价值再与它的规范性. RESTful接口是面向资源的, 而不是面向动作. 比如一个查书的接口, ...
- python实现简单的api接口-Python 实现接口测试的简单实例
Hi~ 由于最近家里宝宝病了,我也在研究python的其他内容,很久没有来社区了,不过我还是一直关注社区的动态哟 好了,闲聊的话题就到此为止, 今天我给大家带来的,是python实现的接口自动化测试的 ...
最新文章
- Xcode 添加前缀
- oracle 12c 自动任务,Oracle job自动任务实用指南
- (第一课)Python学习之蟒蛇绘制
- CodeForces - 1561E Bottom-Tier Reversals(构造)
- UVA211 TheDomino Effect 多米诺效应
- 『设计模式』一张图告诉你UML图怎么画❀
- Cisco 3550-EMI 交 换 机 配 置 教 程
- C和指针之函数之求参数列表中的最大值
- 微信小程序 引用其他js里的方法
- python3设置编码_python3 中文乱码与默认编码格式设定方法
- linux4.9下alsa架构,[Alsa]4, wm8524 Kernel音频子系统入口
- 35岁以后,被社会无情抛弃,放下面子赚钱
- IOS多类型Cell的tableView实现
- AtCoder Beginner Contest 132 解题报告
- Volatile关键字,你真的理解吗?
- WPF简单实用方法(持续更新)
- vue项目中报常见错误
- 睡眠 应该用 a加权 c加权_?焦虑自测 ,看看你的焦虑程度到底有多深?是否影响睡眠?...
- 计算机桌面提示区,win7如何把电脑桌面分成四个区域?电脑分区域显示方法
- 【高等教育6年+工作5年】的个人阶段性自述
热门文章
- 冒泡排序和选择排序的实现与比较
- C++基础学习笔记:第一章、第二章
- stm32定时器编码器模式原理及配置
- cmd管道无法接收特定程序返回值_CQRS amp; Event Sourcing — 解决检索应用程序状态问题的一剂良方...
- Vuex getters 基础使用
- Flutter 进阶篇-所有知识点架构
- python的shelve库
- Python—字典(当索引不好用时)
- 你真的会使用SQL Server的备份还原功能吗?之一:恢复模型
- discuz x2.5 广告位开发学习(第一步:摸索)