Tornado 和现在的主流 Web 服务器框架(包括大多数 Python 的框架)有着明显的区别:它是非阻塞式服务器,而且速度相当快。得利于其 非阻塞的方式和对 epoll 的运用,Tornado 每秒可以处理数以千计的连接,这意味着对于实时 Web 服务来说,Tornado 是一个理想的 Web 框架。
有个朋友让我搞搞tornado框架,说实话,这个框架我用的不多。。。
请大家多关注下,我的原文博客,地址是 blog.xiaorui.cc
我就把自己的一些个运维研发相关的例子,分享给大家。
怎么安装tornado,我想大家都懂。
1
|
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的方式
1
2
3
4
5
6
7
8
9
10
|
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的方式
1
2
3
4
5
6
7
8
9
|
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。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
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'
] = sum
return
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状态码的处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
@tornado.web.asynchronous
def post(self):
""
"Handle POST requests."
""
# Disable caching
self.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()
|
更详细的参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
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)
#获取path
i[
"path"
]=self.request.path
#获取headers
i[
"headers"
]=storage(self.request.headers)
return
i
|
再来一个例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
from datetime
import
date
import
tornado.
escape
import
tornado.ioloop
import
tornado.web
class
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里面的
1
2
3
4
|
class
MainHandler(tornado.web.RequestHandler):
def
get
(self):
items = [
"Item 1"
,
"Item 2"
,
"Item 3"
]
self.render(
"template.html"
, title=
"My title"
, items=items)
|
模板里面的
1
2
3
4
5
6
7
8
9
10
11
12
|
<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用来支持异步。
1
2
3
4
5
6
7
8
|
def MainHandler(tornado.web.RequestHandler):
@tornado.web.asynchronous
def
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才有反应。。。
反应慢的原因是
1
2
3
4
5
6
7
|
class
SleepHandler(tornado.web.RequestHandler):
@tornado.web.asynchronous
@tornado.gen.coroutine
def
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 给了我们用同步代码来做异步实现的可能。
1
2
3
4
5
6
7
|
class
GenAsyncHandler(RequestHandler):
@asynchronous
@gen.engine
def
get
(self):
http_client = AsyncHTTPClient()
response = yield gen.Task(http_client.fetch,
"http://xiaorui.cc"
)
self.render(
"template.html"
)
|
需要注意的是 下面这个是同步的机制
1
|
http_client = httpclient.HTTPClient()
|
要改成异步的话,http_client = httpclient.AsyncHTTPClient()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
import
tornado.ioloop
as
ioloop
import
tornado.httpclient
as
httpclient
import
time
start = time.time()
step =
3
;
def handle_request(response):
global step
if
response.error:
print
"Error:"
, response.error
else
:
print response.body
step -=
1
if
not step:
finish()
def finish():
global start
end = 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代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
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 ii
bb=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
)
|