本节学习目标:
(1)了解Tornado的特点
(2)了解Tornado工作流程
(3)掌握Tornado在Window及Linux中的安装
(4)理解同步、异步
(5)协程基础编程
本节课程内容:
一、Tornado介绍
Tornado是使用Python编写的一个强大的可扩展的Web服务器。除了FriendFeed和Facebook外,还有很多公司在生产上转向Tornado,包括Quora、Turntable.fm、Bit.ly及MyYearbook等。
相对于其它Python的网络框架,Tornado有如下特点:
- 完备的Web框架:与Django、Flask框架等一样,Tornado也提供了URL路由映射、Request上下文、基于模板引擎的页面渲染技术等开发Web应用的必备工具。
- 非阻塞式服务器且速度相当快:tornado每秒可以处理数以千计的连接,其得利于非阻塞的方式和对epoll的运用。
- 高效的网络库:其性能可以与Twisted、Gevent等底层Python框架相媲美,提供了异步I/O支持、超时时间处理。这使得Tornado除了可以作为web服务器框架,还可以用来做爬虫应用、物联网网关、游戏服务器等后台应用。
- 提供高效HTTPClient:除了服务器端框架,tornado还提供了基于异步框架的HTTP客户端。
- 提供高效的内部HTTPServer:虽然其他Python网络框架(Django、Flask)也提供了内部HTTP服务器,但他们由于性能原因只能用于测试环境。而tornado的HTTPServer与tornado异步调用紧密结合,可以直接用于生成环境。
完备的WebSocket支持:WebSocket是HTML5的一种新标准,实现了浏览器和服务器之间的双向实时通信。
根据以上特点:Tornado常被用作大型站点的接口服务框架,而不像Django那样着眼于建立完整的大型网站。除此之外,Tornado还常用于异步及协程编程、身份认证框架、独特的非WSGI部署方式等等。
二、Tornado工作流程图
三、安装Tornado
Tornado已经被配置到PyPI中,在Windows和Linux中都可以通过一条pip命令完成安装#pip install tornado
该条命令运行在Linux操作系统中。安装结果如下图所示:
四、同步/异步及yield
协程是Tornado中推荐的变成方式,使用协程可以开发出简捷、高效的异步处理代码。首先要掌握的内容是:
(1) 同步I/O和异步I/O
- 同步I/O(synchronous Input/Output operation):
导致请求进程阻塞,直到I/O操作完成。在Python中,同步I/O可以被理解为一个被调用的I/O函数会阻塞调用函数的执行。 - 异步I/O(asynchronous Input/Output operation):
不会导致请求进程阻塞。可以理解为一个被调用的I/O函数不会阻塞调用函数的执行。
代码举例如下
#同步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的主事件循环对象【IOLoop会在后面有专门的章节去讲解】,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()调用没有返回值的协程函数。