本節學習目標:
(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()調用沒有返回值的協程函數。