logo头像

Always believe youself.

初识Tornado

本文于1625天之前发表,文中内容可能已经过时。

Tornado是使用Python编写的一个强大的可扩展的Web服务器

tornado 框架特点:

  • 完备的web框架:
    与django,Flask框架等一样,Tornado也提供了URL路由映射,request 上下文,基于模板引擎的页面渲染技术等开发web应用 的必备工具。
  • 非阻塞式服务器且速度相当快:
    tornado每秒可以处理数以千计的链接,基于利于非阻塞的方式和对epoll的运用。
  • 高效的网络库:
    其性能可以与Twisted,Gevent等底层python框架相媲美,提供了异步I/O支持。超时时间处理。这使得Tornado除了可以作为web服务器框架,还可以来做爬虫应用,物联网网关,游戏服务器等后台应用。
  • 提供高效的HTTPClient:
    除了服务器端框架,tornado还提供了基于异步框架的HTTP客户端。
  • 提供高效的内部HTTPSever:
    虽然其他python网络框架(Django,Flask)也提供了内部HTTP服务器,但他们由于性能原因只能用于测试环境。而tornado的HTTPserver与tornado异步调用紧密结合,可以直接用于生成环境。
  • 完备的websocket支持:
    websocket是HTML5 的一种新的标准,实现了浏览器和服务器之间的双向 实时通信。

根据以上特点:

Tornado常被用作大型站点的接口服务框架,而不像Django那样着眼于建立完整的大型网站。除此之外,Tornado还常用于异步及协程编程、身份认证框架、独特的非WSGI部署方式等等

工作流程图

流程图

同步和异步及yield

协程是Tornado中推荐的编程方式,使用协程可以开发出简捷、高效的异步处理代码

1) 同步IO 和 异步IO

- 同步IO:导致请求进程阻塞,直到IO操作完成。在python中,同步IO可以被理解为一个被调用 的函数会阻塞调用函数的执行。
- 异步IO:不会导致请求进程阻塞。可以理解为一个被调用的IO函数不会阻塞调用函数的执行。
  • 代码举例如下:

    #同步I/O — 使用HTTPClinet客户端
    from tornado.httpclient import HTTPClient #tornado的同步HTTP客户端类
    print ‘访问前’
    def syn_visit():
    http_client = HTTPClient()
    print ‘访问中…’
    response = http_client.fetch(“http://www.lanou3g.com") #阻塞,直到访问完成
    print response.body
    syn_visit()
    print ‘访问后’

HTTPClinet是tornado的同步访问HTTP客户端。

上述代码中的synchronous_visit()函数使用了典型的同步I/O操作访问www.baidu.com网站,该函数的执行时间取决于网络速度、服务器响应速度等,只有对网站访问完成并获取响应结果后,才能完成synchronous_visit()整个函数的执行。

  • 异步代码举例如下:

    #异步I/O — 使用AsyncHTTPClient客户端
    from tornado.httpclient import AsyncHTTPClient #tornado的异步HTTP客户端类
    def handle_response(response):
    print response.body
    print ‘访问前’
    def asyn_visit():
    http_client = AsyncHTTPClient()
    print ‘访问中’
    response = http_client.fetch(“http://www.lanou3g.com",callback=handle_response) #不会阻塞进程
    asyn_visit()
    print ‘访问后’

AsyncHTTPClient是Tornado的异步访问HTTP客户端。

代码中的asyn_visit()函数中使用AsyncHTTPClient对第三方网站进行异步访问,http_client.fetch()函数会在调用后立刻返回而无须等待实际访问的完成,从而导致asyn_visit()也会立刻执行完成。当访问实际完成后,AsyncHTTPClient会调用callback参数指定的函数。

2) python 关键字 yield

- 协程是Tornado中进行异步I/O代码开发的方法。
- 协程使用了Python关键字yield将调用者挂起和恢复执行。所以在学习协程编程之前,首先掌握Python中yield关键字的使用。
- 迭代器在Python编程中适用范围很广,那么开发者如何定制自己的迭代器呢?答案就是使用yield关键字。

 ` 当调用任何定义中包含yield关键字的函数都不会执行该函数,而会或得一个对应函数的迭代器。 `

示例代码:

    #自定义迭代器函数
    def demoIterator():
        print "The first call of next()"
        yield 'number 1'
        print "The second call of next()"
        yield 'number 2'
        print "The third call of next()"
        yield 'number3'
    print type(demoIterator())  #<type 'generator'>
    for i in demoIterator():
        print i
使用for循环会每次调用迭代器的next()函数,将执行迭代器函数,并返回yield的结果作为迭代返回元素。

协程编程基础

使用Tornado协程可以开发出类似同步代码的异步行为。同时,因为协程本身不使用线程,所以减少了线程上下文切换的开销。

1) 协程函数的编写
实例代码:

# -*- coding: utf-8 -*-
from tornado import gen  #引入协程库gen
from tornado.httpclient import AsyncHTTPClient #引入框架中的异步客户端
@gen.coroutine
def cor_visit():
    http_client = AsyncHTTPClient()
    response = yield http_client.fetch('http://www.lanou3g.com')
    print response.body

上述代码中,仍然使用了异步客户端AsyncHTTPClient进行页面访问,修饰器@gen.coroutine声明其是一个协程函数。由于使用了yield关键字,所以代码中不用再编写回调函数用于处理访问结果,而可以直接在yield语句的后面编写结果处理语句。
2) 协程函数的使用
由于tornado协程基于python的yield关键字实现,所以不能像调用一般函数去调用。

调用协程函数的方式有三种:

(1) 第一种方式:

在本身是协程的函数内调用另一个协程函数,可以通过yield关键字调用。

@gen.coroutine
def use_cor():
    print "开始调用其它协程函数"
    yield cor_visit()
    print "结束调用其它协程函数"

(2) 第二种方式:

在IOLoop尚未启动时,通过IOLoop的run_sync()函数。

from tornado import gen # 引入协程库
from tornado.ioloop import IOLoop
from tornado.httpclient import AsyncHTTPClient
def loop_stop_visit():
    print('开始调用协程')
    IOLoop.current().run_sync(lambda: cor_visit())
    print('结束协程调用')
loop_stop_visit()

其中,IOLoop 是Tornado的主事件循环对象,Tornado程序通过它监听外部客户端的访问请求,并执行相应的操作,当程序尚未进入IOLoop的runing状态时,可以通过run_sync()函数调用协程函数。run_sync()函数会将阻塞当前函数的执行,直到被调用的协程函数执行完成。

(2) 第三种方式:

在IOLoop已经启动时,通过IOLoop的spawn_callback()函数调用。

def loop_start_visit():
    IOLoop.current().spawn_callback(coroutine_visit)
loop_start_visit()    #调用

上述代码中,spawn_callback()函数不会等待被调用的协程函数执行完成,所以spawn_callback() 之前和之后的语句会被连续执行,而cor_visit本身将会有IOLoop在合适的时机进行调用,并且spawn_callback 函数没有为开发者提供获取协程函数调用函数返回值的方法,所以只能用spawn_callback()调用没有返回值的协程函数。