Tornado 介绍
Tornado 是 FriendFeed 使用的可扩展的非阻塞式 web 服务器及其相关工具的开源版本。这个 Web 框架看起来有些像web.py 或者 Google 的 webapp,不过为了能有效利用非阻塞式服务器环境,这个 Web 框架还包含了一些相关的有用工具 和优化。
Tornado 和现在的主流 Web 服务器框架(包括大多数 Python 的框架)有着明显的区别:它是非阻塞式服务器,而且速度相当快。得利于其 非阻塞的方式和对 epoll 的运用,Tornado 每秒可以处理数以千计的连接,这意味着对于实时 Web 服务来说,Tornado 是一个理想的 Web 框架。我们开发这个 Web 服务器的主要目的就是为了处理 FriendFeed 的实时功能 ——在 FriendFeed 的应用里每一个活动用户都会保持着一个服务器连接。(关于如何扩容 服务器,以处理数以千计的客户端的连接的问题,请参阅 C10K problem。)
下载安装
pip install tornado
源码安装
https://pypi.python.org/packages/source/t/tornado/tornado-4.3.tar.gz
快速使用
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import tornado.ioloop
import tornado.web
# 继承tornado.web.RequestHandler
class MainHandler(tornado.web.RequestHandler):
# 如果用户是GET请求,将执行这个类的get方法
def get(self):
self.write("Hello, world")
# 路由映射
application = tornado.web.Application([
# 访问index页面,交由MainHandler类处理
(r"/index", MainHandler),
])
if __name__ == "__main__":
# 内部创建了个socket,侦听8888端口
application.listen(8888)
# epool + socket
tornado.ioloop.IOLoop.instance().start()
执行过程:
- 第一步:运行程序,启动 8888 端口侦听
- 第二步:浏览器客户端访问 /index –> http://127.0.0.1:8888/index
- 第三步:服务器接受请求,并交由对应的类处理该请求
- 第四步:类接受到请求之后,根据请求方式(post / get / delete …)的不同调用并执行相应的方法(Resutful方式)
- 第五步:方法返回值的字符串内容发送到用户浏览器
Tronado 原生支持Resutful
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: Liang Lian
# Python 3.5
import tornado.ioloop
import tornado.web
class MainHandler(tornado.web.RequestHandler):
# 不同类型的请求执行下面相应的方法
def get(self):
self.write("GET, Test Site")
def post(self):
self.write("POST, Test Site")
def delete(self):
self.write("DEL, Test Site")
def put(self):
self.write("PUT, Test Site")
# 内部其实就是通过反射实现的
# obj = MainHandler()
# func = getattr(obj, 'get')
# func()
application = tornado.web.Application([
(r"/index", MainHandler),
])
if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
处理表单请求
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: Liang Xian Sen
# Python 3.5
import tornado.ioloop
import tornado.web
class MyFormHandler(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("You wrote " + self.get_body_argument("message")) # 获取form表单内容
def make_app():
return tornado.web.Application([
(r"/", MyFormHandler),
])
if __name__ == "__main__":
app = make_app()
app.listen(8888)
tornado.ioloop.IOLoop.current().start()
POST application/json
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: Liang Xian Sen
# Python 3.5
import json
import tornado.ioloop
import tornado.web
class BaseHandler(tornado.web.RequestHandler):
def initialize(self):
self.json_args = None
def prepare(self):
"""
复习接收到请求后做的准备操作,然后再执行HTTP方法(post/get/...)。
:return:
"""
if self.request.headers["Content-Type"].startswith("application/json"):
self.json_args = json.loads(self.request.body)
else:
# 我这个接口只定义是传输数据类型是json, 那么在处理请求前做一个json处理
# 如果不是json 数据类型就直接设置一个错误状态码,返回一个错误信息。
self.set_status(405)
self.write({"error": "ARGUMENT_ERROR: Not application/json"})
self.finish() # 关闭链接
def on_finish(self):
"""
请求结束后被调用,一般用于清理资源
:return:
"""
pass
class MainHandler(BaseHandler):
def post(self):
if self.json_args:
self.write(self.json_args) # 如果数据类型就是Json,response数据类型application/json
def make_app():
return tornado.web.Application([
(r"/", MainHandler),
])
if __name__ == "__main__":
app = make_app()
app.listen(8888)
tornado.ioloop.IOLoop.current().start()
Flush mothed
将当前输出缓冲区写到网络.
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: Liang Xian Sen
# Python 3.5
import time
from tornado.web import RequestHandler, Application
from tornado.ioloop import IOLoop
class FlushHandler(RequestHandler):
def get(self, *args, **kwargs):
self.write('start\n')
self.flush()
for x in range(5):
self.write('{0}\n'.format(x))
self.flush() # 这里是一个for的循环体,这个方法可以将上面的write实时显示到用户浏览器上,如果去掉,你会发现整个循环体结束然后将最终结果显示在用户浏览器上。
time.sleep(1)
self.finish('stop\n')
def make_app():
return Application([
(r'/flush', FlushHandler),
])
if __name__ == '__main__':
app = make_app()
app.listen(port=8888)
IOLoop.current().start()
路由系统
路由系统其实就是 url 和 类 的对应关系,这里不同于其他框架,其他很多框架均是 url 对应函数,Tornado中每个url对应的是一个类。
示例代码
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import tornado.ioloop
import tornado.web
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")
class CMDBHandler(tornado.web.RequestHandler):
def get(self):
self.write("cmdb.lianglian.com")
application = tornado.web.Application([
(r"/index", MainHandler),
])
# 添加一个二级域名
application.add_handlers('cmdb.lianglian.com', [
(r'/index', CMDBHandler),
])
if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
Note: 本地测试先配置好系统hosts文件域名指向
路由表是 URLSpec
对象(或元组)的列表, 其中每个都包含(至少)一个正则 表达式和一个处理类. 顺序问题; 第一个匹配的规则会被使用. 如果正则表达 式包含捕获组, 这些组会被作为 路径参数 传递给处理函数的HTTP方法. 如果一个字典作为 URLSpec
的第三个参数被传递, 它会作为 初始参数 传递给 RequestHandler.initialize
. 最后 URLSpec
可能有一个名字 , 这将允许它被 RequestHandler.reverse_url
使用.
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: Liang Xian Sen
# Python 3.5
import tornado.ioloop
import tornado.web
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write('<a href="%s">link to story 1</a>' % self.reverse_url("story", "1")) # /story/1
class StoryHandler(tornado.web.RequestHandler):
def initialize(self):
pass
def get(self, story_id):
self.write("this is story %s" % story_id)
def make_app():
return tornado.web.Application([
tornado.web.url(r"/", MainHandler),
# Tornado通过正则表达式做到分页
tornado.web.url(r"/story/([0-9]+)", StoryHandler, name="story") # 设置name属性可以在self.reverse_url被调用
])
if __name__ == "__main__":
app = make_app()
app.listen(8888)
tornado.ioloop.IOLoop.current().start()
二级域名
Tornado中原生支持二级域名的路由,如:
主机头 | url正则 | Handler |
---|---|---|
cmdb | /index/\d* | CMDBHandler |
cmdb | /admin/\w* | AdminHandler |
cmdb | /car/\w* | CarHandler |
*. | /index/\w* | HomeHandler |
*. | /pro/\w* | ProHandler |
*. | /.* | AllHandler |
模板语言
Tornado中的模板语言和django中类似,模板引擎将模板文件载入内存,然后将数据嵌入其中,最终获取到一个完整的字符串,再将字符串返回给请求者。
Tornado 的模板支持“控制语句”和“表达语句”,控制语句是使用{% 和 %}包起来的 例如 {% if len(items) > 2 %}。表达语句是使用 {{ 和 }} 包起来的,例如 {{ items[0] }}。
控制语句和对应的 Python 语句的格式基本完全相同。我们支持 if、for、while 和 try,这些语句逻辑结束的位置需要用 {% end %} 做标记。还通过 "extends" 和 "block" 语句实现了模板继承。这些在 template 模块 的代码文档中有着详细的描述。
注:在使用模板前需要在setting中设置模板路径:”template_path” : “Templates”
模板渲染
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import tornado.ioloop
import tornado.web
class MainHandler(tornado.web.RequestHandler):
def get(self):
# self.write("Hello, world")
self.render('main.html', li=[11, 22, 33, 44]) # 返回一个html页面和一个数组"li"
# 配置模板文件路径
settings = {
'template_path': 'Templates', # 本地Templates文件夹
}
application = tornado.web.Application([
(r"/index", MainHandler),
], **settings) # 把settings这个字典传给application,配置生效
if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
Tornado中模板语言里面语法和python基本一样,这也是Tornado前端用python语法直接写逻辑很强大。
在模板中默认提供了一些函数、字段、类以供模板使用:
escape: tornado.escape.xhtml_escape 的別名
xhtml_escape: tornado.escape.xhtml_escape 的別名
url_escape: tornado.escape.url_escape 的別名
json_encode: tornado.escape.json_encode 的別名
squeeze: tornado.escape.squeeze 的別名
linkify: tornado.escape.linkify 的別名
datetime: Python 的 datetime 模组
handler: 当前的 RequestHandler 对象
request: handler.request 的別名
current_user: handler.current_user 的別名
locale: handler.locale 的別名
_: handler.locale.translate 的別名
static_url: for handler.static_url 的別名
xsrf_form_html: handler.xsrf_form_html 的別名
模版继承
Tornado中的模版继承和Django中一样,通过一个大的母版,规定一个大体框架内容,子版继承母版得内容后只需要写自己的内容就行了。
母版(main.html)
子版
模板导入
- header.html
<div>
<ul>
<li>1024</li>
<li>42区</li>
</ul>
</div>
- index.html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>Index</title>
<link href="{{static_url("css/common.css")}}" rel="stylesheet" />
</head>
<body>
<div class="pg-header">
{% include 'header.html' %} # 导入header.html得内容到这里
</div>
<script src="{{static_url("js/jquery-1.8.2.min.js")}}"></script>
</body>
</html>
UIMethod和UIModule
- 定义
# uimethods.py
def tab(self):
return 'UIMethod'
# uimodules.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from tornado.web import UIModule
from tornado import escape
class custom(UIModule):
def render(self, *args, **kwargs):
return escape.xhtml_escape('<h1>wupeiqi</h1>')
#return escape.xhtml_escape('<h1>wupeiqi</h1>')
- 注册
import tornado.ioloop
import tornado.web
import uimodules # 导入刚刚写的uimodules.py
import uimethods # 导入刚刚写的uimodules.py
class MainHandler(tornado.web.RequestHandler):
def get(self):
# self.write("Hello, world")
self.render('index.html', li=[11, 22, 33, 44]) # 返回一个html页面和一个数组"li"
# 配置
settings = {
'template_path': 'Templates',
'static_path': 'static',
'static_url_prefix': '/statics/',
'ui_methods': uimethods, # 在settings中注册我写的uimethods.py
'ui_modules': uimodules, # 在settings中注册我写的uimodules.py
}
application = tornado.web.Application([
(r"/index", MainHandler),
], **settings) # 把settings这个字典传给application,配置生效
if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
- 使用
协程
Tornado默认是单进程单线程。实时的web特性通常需要为每个用户一个大部分时间都处于空闲的长连接. 在传统的同步web服务器中,这意味着需要给每个用户分配一个专用的线程,这样的开销是十分巨大的.
为了减小对于并发连接需要的开销,Tornado使用了一种单线程事件循环的方式. 这意味着所有应用程序代码都应该是异步和非阻塞的,因为在同一时刻只有一个操作是有效的.
Tornado 中推荐用 协程 来编写异步代码. 协程使用 Python 中的关键字 yield
来替代链式回调来实现挂起和继续程序的执行(像在 gevent 中使用的轻量级线程合作的方法有时也称作协程, 但是在 Tornado 中所有协程使用异步函数来实现的明确的上下文切换).
同步阻塞(Blocking)
一个函数通常在它等待返回值的时候被 阻塞 .一个函数被阻塞可能由于很多原因: 网络I/O,磁盘I/O,互斥锁等等.事实上, 每一个 函数都会被阻塞,只是时间会比较短而已, 当一个函数运行时并且占用CPU(举一个极端的例子来说明为什么CPU阻塞的时间必须考虑在内, 考虑以下密码散列函数像bcrypt, 这个函数需要占据几百毫秒的CPU时间, 远远超过了通常对于网络和磁盘请求的时间). 一个函数可以在某些方面阻塞而在其他方面不阻塞.举例来说, tornado.httpclient
在默认设置下将阻塞与DNS解析,但是在其它网络请求时不会阻塞 (为了减轻这种影响,可以用 ThreadedResolver
或通过正确配置 libcurl
使用 tornado.curl_httpclient
). 在Tornado的上下文中我们通常讨论网络I/O上下文阻塞, 虽然各种阻塞已经被最小化了.
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: Liang Lian
# Python 3.5
import time
import tornado.web
class IndexHandler(tornado.web.RequestHandler):
def get(self):
self.write('index')
def doing():
time.sleep(10)
return 'Blocking'
class BlockingHandler(tornado.web.RequestHandler):
def get(self):
result = doing()
self.write(result)
application = tornado.web.Application([
(r"/index", IndexHandler),
(r"/blocking", BlockingHandler),
])
if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
- 浏览器访问:http://127.0.0.1:8888/index
- 浏览器访问:http://127.0.0.1:8888/blocking
- 你会发现blocking会一直在转圈,处于一个堵塞状态。
- 你再访问index页面,你发现index页面也会堵塞住。
异步非阻塞(Non Blocking)
一个 异步 函数在它结束前就已经返回了,而且通常会在程序中触发一些动作然后在后台执行一些任务. (和正常的 同步 函数相比, 同步函数在返回之前做完了所有的事). 这里有几种类型的异步接口:
- 回调函数
- 返回一个占位符 (
Future
,Promise
,Deferred
) - 传送一个队列
- 回调注册 (例如. POSIX 信号)
不论使用哪一种类型的接口, 依据定义 异步函数与他们的调用者有不同的交互方式; 但没有一种对调用者透明的方式可以将同步函数变成异步函数 (像 gevent 通过一种轻量的线程库来提供异步系统,但是实际上它并不能让事情变得异步)
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: Liang Xian Sen
# Python 3.5
import tornado.web
from tornado import gen
class IndexHandler(tornado.web.RequestHandler):
def get(self):
self.write('index')
@gen.coroutine
def doing():
"""
穿上@gen.coroutine 装饰器之后,最终结果会返回一个可以被yield 的生成器 Future 对象
与众不同的是这个函数的返回值需要以 raise gen.Return() 这种形式返回。
:return: Future object
"""
# time.sleep(10) # time.sleep() 是blocking 的,不支持异步操作,我刚开始测试tornado的时候坑了
yield gen.sleep(10) # 使用这个方法代替上面的方法模拟 I/O 等待的情况, 可以点进去看下这个方法的介绍
raise gen.Return('Non-Blocking')
class NonBlockingHandler(tornado.web.RequestHandler):
@gen.coroutine
def get(self):
result = yield doing()
self.write(result)
application = tornado.web.Application([
(r"/index", IndexHandler),
(r"/nonblocking", NonBlockingHandler),
])
if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
- 浏览器访问:http://127.0.0.1:8888/nonblocking
- 浏览器访问:http://127.0.0.1:8888/index
- 你会发现nonblocking会一直在转圈,处于一个堵塞状态。
- 你再访问index页面,你发现index页面能够访问不受影响。
包含了 yield
关键字的函数是一个 生成器(generator). 所有的生成器都是异步的; 当调用它们的时候,会返回一个生成器对象,而不是一个执行完的结果. `@gen.coroutine装饰器通过
yield表达式和生成器进行交流, 而且通过返回一个 [
Future](http://tornado-zh.readthedocs.io/zh/latest/concurrent.html#tornado.concurrent.Future) 与协程的调用方进行交互. 协程一般不会抛出异常: 它们抛出的任何异常将被 [
Future](http://tornado-zh.readthedocs.io/zh/latest/concurrent.html#tornado.concurrent.Future) 捕获 直到它被得到. 这意味着用正确的方式调用协程是重要的, 否则你可能有被 忽略的错误。
@gen.coroutine可以让你的函数以异步协程的形式运行,但是依赖第三方的异步库,要求你的函数本身不是Booking的。例如上面的
os.sleep()` 方法是blocking 的,没办法实现异步非阻塞。
Future
上面提到Future 到底是什么呢,原始的 Future
版本十分复杂, 但是 Futures
是 Tornado 中推荐使用的一种做法, 因为它有两个主要的优势. 错误处理时通过 Future.result
函数可以简单的抛出一个异常 (不同于某些传统的基于回调方式接口的 一对一的错误处理方式), 而且 Futures
对于携程兼容的很好. 我们这里简单使用一下future 写一个异步函数。
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: Liang Xian Sen
# Python 3.5
import tornado.web
from tornado import gen
from tornado.concurrent import Future
class IndexHandler(tornado.web.RequestHandler):
def get(self):
self.write('index')
def doing():
future = Future()
# here doing some things ...
future.set_result('Non-Blocking')
return future
class NonBlockingHandler(tornado.web.RequestHandler):
@gen.coroutine
def get(self):
result = yield doing()
self.write(result)
application = tornado.web.Application([
(r"/index", IndexHandler),
(r"/nonblocking", NonBlockingHandler),
])
if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
Python 3.5: async
and await
官方还介绍了在另一种写法, Python 3.5 引入了 async
和 await
关键字(使用这些关键字的 函数也被称为”原生协程”). 从Tornado 4.3, 你可以用它们代替 yield
为基础的协程. 只需要简单的使用 async def foo()
在函数定义的时候代替 `@gen.coroutine装饰器, 用
await代替yield. 本文档的其他部分会继续使用
yield的风格来和旧版本的Python兼容, 但是如果
async和
await` 可用的话,它们运行起来会更快
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: Liang Xian Sen
# Python 3.5
import tornado.web
from tornado import gen
class IndexHandler(tornado.web.RequestHandler):
def get(self):
self.write('index')
async def doing():
await gen.sleep(10) # here are doing some things
return 'Non-Blocking'
class NonBlockingHandler(tornado.web.RequestHandler):
async def get(self):
result = await doing()
self.write(result)
application = tornado.web.Application([
(r"/index", IndexHandler),
(r"/nonblocking", NonBlockingHandler),
])
if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
并行执行
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: Liang Xian Sen
# Python 3.5
import tornado.web
from tornado import gen
class IndexHandler(tornado.web.RequestHandler):
def get(self):
self.write('index')
@gen.coroutine
def doing():
yield gen.sleep(10)
raise gen.Return('Non-Blocking')
class NonBlockingHandler(tornado.web.RequestHandler):
@gen.coroutine
def get(self):
result1, result2 = yield [doing(), doing()]
self.write(result1)
application = tornado.web.Application([
(r"/index", IndexHandler),
(r"/nonblocking", NonBlockingHandler),
])
if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
那async ,await 那种方式能并行执行吗? 答案也是可以的:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: Liang Xian Sen
# Python 3.5
# Date: 2017/12/13
import tornado.web
from tornado import gen
class IndexHandler(tornado.web.RequestHandler):
def get(self):
self.write('index')
async def doing():
await gen.sleep(10)
return 'Non-Blocking'
class NonBlockingHandler(tornado.web.RequestHandler):
async def get(self):
result1, result2 = await gen.convert_yielded([doing(), doing()])
self.write(result1)
application = tornado.web.Application([
(r"/index", IndexHandler),
(r"/nonblocking", NonBlockingHandler),
])
if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
await
关键字比 yield
关键字功能要少一些. 例如,在一个使用 yield
的协程中, 你可以得到Futures
列表, 但是在原生协程中,你必须把列表用 tornado.gen.multi
包起来. 你也可以使用 tornado.gen.convert_yielded
来把任何使用 yield
工作的代码转换成使用 await
的形式.
进程池
coroutine
是给Non-blocking 函数提供异步协程的方式运行, ThreadPoolExecutor
则是通过多进程给blocking 的函数提供非阻塞的方式运行,但是由于是多线程的,Python 使用多线程对新能来说是需要谨慎的,大量的计算量的情况可能会造成性能的下降。
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: Liang Xian Sen
# Python 3.5
import time
import os
import tornado.web
from tornado import gen
from tornado.concurrent import run_on_executor
from concurrent.futures import ThreadPoolExecutor
class IndexHandler(tornado.web.RequestHandler):
def get(self):
self.write('index')
self.write('index')
print('index')
class NonBlockingHandler(tornado.web.RequestHandler):
executor = ThreadPoolExecutor(4)
@gen.coroutine
def get(self):
result = yield self.doing()
self.write(result)
print(result)
# 使用tornado 线程池不需要加上下面的装饰器到I/O函数
@run_on_executor
def doing(self):
# time.sleep(10)
# yield gen.sleep(10)
os.system("ping -c 20 www.baidu.com") # 模拟I/O 任务
return 'Non-Blocking'
application = tornado.web.Application([
(r"/index", IndexHandler),
(r"/nonblocking", NonBlockingHandler),
])
if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
设置超时时间
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: Liang Xian Sen
# Python 3.5
import time
import datetime
import os
import tornado.web
from tornado import gen
from tornado.concurrent import run_on_executor
from concurrent.futures import ThreadPoolExecutor
class IndexHandler(tornado.web.RequestHandler):
def get(self):
self.write('index')
print('index')
class NonBlockingHandler(tornado.web.RequestHandler):
executor = ThreadPoolExecutor(4)
@gen.coroutine
def get(self):
try:
start = time.time()
# 并行执行
result1, result2 = yield gen.with_timeout(datetime.timedelta(seconds=5), [self.doing(1), self.doing(2)], quiet_exceptions=tornado.gen.TimeoutError)
self.write("NO Timeout")
print(result1, result2)
print(time.time() - start)
except gen.TimeoutError:
self.write("Timeout")
print("Timeout")
print(time.time() - start)
# 使用tornado 线程池需要加上下面的装饰器到I/O函数
@run_on_executor
def doing(self, num):
time.sleep(10)
return 'Non-Blocking%d' % num
application = tornado.web.Application([
(r"/index", IndexHandler),
(r"/nonblocking", NonBlockingHandler),
])
if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
多进程运行
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: Liang Xian Sen
# Python 3.5
import tornado.web
from tornado import gen
from tornado.httpserver import HTTPServer
class IndexHandler(tornado.web.RequestHandler):
def get(self):
self.write('index')
@gen.coroutine
def doing():
yield gen.sleep(10)
raise gen.Return('Non-Blocking')
class NonBlockingHandler(tornado.web.RequestHandler):
@gen.coroutine
def get(self):
result = yield doing()
self.write(result)
def make_app():
return tornado.web.Application([
(r"/index", IndexHandler),
(r"/nonblocking", NonBlockingHandler),
])
def main():
app = make_app()
server = HTTPServer(app)
server.bind(8888)
server.start(2) # 设置启动多少个进程
tornado.ioloop.IOLoop.current().start()
if __name__ == "__main__":
main()