Python Web框架之Tornado

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()

异步非阻塞(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()

包含了 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 引入了 asyncawait 关键字(使用这些关键字的 函数也被称为”原生协程”). 从Tornado 4.3, 你可以用它们代替 yield 为基础的协程. 只需要简单的使用 async def foo() 在函数定义的时候代替 `@gen.coroutine装饰器, 用await代替yield. 本文档的其他部分会继续使用yield的风格来和旧版本的Python兼容, 但是如果asyncawait` 可用的话,它们运行起来会更快

#!/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()
文章目录
  1. 1. Tornado 介绍
    1. 1.1. 下载安装
    2. 1.2. 快速使用
    3. 1.3. Tronado 原生支持Resutful
    4. 1.4. 处理表单请求
    5. 1.5. POST application/json
    6. 1.6. Flush mothed
  2. 2. 路由系统
    1. 2.1. 示例代码
    2. 2.2. 二级域名
  3. 3. 模板语言
    1. 3.1. 模板渲染
    2. 3.2. 模版继承
      1. 3.2.1. 母版(main.html)
      2. 3.2.2. 子版
    3. 3.3. 模板导入
    4. 3.4. UIMethod和UIModule
  4. 4. 协程
    1. 4.1. 同步阻塞(Blocking)
    2. 4.2. 异步非阻塞(Non Blocking)
    3. 4.3. Future
    4. 4.4. Python 3.5: async and await
    5. 4.5. 并行执行
    6. 4.6. 进程池
    7. 4.7. 设置超时时间
  5. 5. 多进程运行
|