【網(wǎng)站構(gòu)建】Python高性能架構(gòu)Tornado
Start:關(guān)注本公眾號(hào)后,可直接聯(lián)系后臺(tái)獲取排版美化的詳細(xì)文檔!
Hints:本篇文章所編纂的資料均來(lái)自網(wǎng)絡(luò),特此感謝參與奉獻(xiàn)的有關(guān)人員。
Tornado的簡(jiǎn)單概述:
Tornado是一個(gè)基于Python的Web服務(wù)框架和異步網(wǎng)絡(luò)庫(kù)。Tornado 是一個(gè)基于Python的Web服務(wù)框架和異步網(wǎng)絡(luò)庫(kù)。由facebook開(kāi)源出來(lái)的超高并發(fā)框架,利用異步協(xié)程機(jī)制,實(shí)現(xiàn)比多線(xiàn)程更高的性能爆發(fā),絕對(duì)是高并發(fā)類(lèi)網(wǎng)站的首選,知乎網(wǎng)就是用這個(gè)框架開(kāi)發(fā)的,性能超高。
Tornado的應(yīng)用場(chǎng)景:
-1用戶(hù)量大,高并發(fā)
如秒殺搶購(gòu)、雙十一某寶購(gòu)物、春節(jié)搶火車(chē)票
-2大量的HTTP持久連接
使用同一個(gè)TCP連接來(lái)發(fā)送和接收多個(gè)HTTP請(qǐng)求/應(yīng)答,而不是為每一個(gè)新的請(qǐng)求/應(yīng)答打開(kāi)新的連接的方法。對(duì)于HTTP 1.0,可以在請(qǐng)求的包頭(Header)中添加Connection: Keep-Alive。對(duì)于HTTP 1.1,所有的連接默認(rèn)都是持久連接。
Tornado的主要構(gòu)成:
-Web 框架 (包括用來(lái)創(chuàng)建 Web 應(yīng)用程序的 RequestHandler類(lèi), 還有很多其它支持的類(lèi)).
-HTTP 客戶(hù)端和服務(wù)器的實(shí)現(xiàn) (HTTPServer和 AsyncHTTPClient).
-異步網(wǎng)絡(luò)庫(kù) (IOLoop和 IOStream),對(duì) HTTP 的實(shí)現(xiàn)提供構(gòu)建模塊, 還可以用來(lái)實(shí)現(xiàn)其他協(xié)議.
-協(xié)程庫(kù) (tornado.gen)讓用戶(hù)通過(guò)更直接的方法來(lái)實(shí)現(xiàn)異步編程, 而不是通過(guò)回調(diào)的方式.
Tornado web 框架和 HTTP 服務(wù)器提供了一整套WSGI的方案.可以讓Tornado編寫(xiě)的Web框架運(yùn)行在一個(gè)WSGI容器中 (WSGIAdapter),或者使用 TornadoHTTP 服務(wù)器作為一個(gè)WSGI容器 (WSGIContainer),這兩種解決方案都有各自的局限性, 為了充分享受Tornado為您帶來(lái)的特性,你需要同時(shí)使用Tornado的web框架和HTTP服務(wù)器.
Tornado的關(guān)鍵概念:
異步和非阻塞 I/O
實(shí)時(shí)的web特性通常需要為每個(gè)用戶(hù)一個(gè)大部分時(shí)間都處于空閑的長(zhǎng)連接.在傳統(tǒng)的同步web服務(wù)器中,這意味著需要給每個(gè)用戶(hù)分配一個(gè)專(zhuān)用的線(xiàn)程,這樣的開(kāi)銷(xiāo)是十分巨大的.為了減小對(duì)于并發(fā)連接需要的開(kāi)銷(xiāo),Tornado使用了一種單線(xiàn)程事件循環(huán)的方式.這意味著所有應(yīng)用程序代碼都應(yīng)該是異步和非阻塞的,因?yàn)樵谕粫r(shí)刻只有一個(gè)操作是有效的.
-阻塞
一個(gè)函數(shù)通常在它等待返回值的時(shí)候被阻塞 .一個(gè)函數(shù)被阻塞可能由于很多原因:網(wǎng)絡(luò)I/O,磁盤(pán)I/O,互斥鎖等等.事實(shí)上, 每一個(gè)函數(shù)都會(huì)被阻塞,只是時(shí)間會(huì)比較短而已。
-異步
一個(gè)異步 函數(shù)在它結(jié)束前就已經(jīng)返回了,而且通常會(huì)在程序中觸發(fā)一些動(dòng)作然后在后臺(tái)執(zhí)行一些任務(wù).(和正常的同步 函數(shù)相比, 同步函數(shù)在返回之前做完了所有的事).
-協(xié)程
Tornado 中推薦用 協(xié)程 來(lái)編寫(xiě)異步代碼. 協(xié)程使用 Python 中的關(guān)鍵字 yield來(lái)替代鏈?zhǔn)交卣{(diào)來(lái)實(shí)現(xiàn)掛起和繼續(xù)程序的執(zhí)行。協(xié)程和異步編程的代碼一樣簡(jiǎn)單, 而且不用浪費(fèi)額外的線(xiàn)程. 它們還可以減少上下文切換讓并發(fā)更簡(jiǎn)單.
Tornado的環(huán)境配置:
Pip install tornado
Tornado的HelloWorld
import tornado.ioloop
import tornado.web
# handler類(lèi),它代表著業(yè)務(wù)邏輯,我們進(jìn)行服務(wù)端開(kāi)發(fā)時(shí)就是編寫(xiě)一堆一堆的handler用來(lái)服務(wù)客戶(hù)端請(qǐng)求。
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello,world")
def make_app():
return tornado.web.Application(
# 路由表,它將指定的url規(guī)則和handler掛接起來(lái),形成一個(gè)路由映射表。當(dāng)請(qǐng)求到來(lái)時(shí),根據(jù)請(qǐng)求的訪(fǎng)問(wèn)url查詢(xún)路由映射表來(lái)找到相應(yīng)的業(yè)務(wù)handler。
[
(r"/", MainHandler),
]
)
if __name__ == "__main__":
# app實(shí)例,它代表著一個(gè)完成的后端app,它會(huì)掛接一個(gè)服務(wù)端套接字端口對(duì)外提供服務(wù)。
app = make_app()
# 后端app監(jiān)聽(tīng)端口設(shè)置
app.listen(8888)
# ioloop實(shí)例,全局的tornado事件循環(huán)
tornado.ioloop.IOLoop.current().start()
一個(gè)普通的tornado web服務(wù)器通常由四大組件組成。
-1.ioloop實(shí)例,它是全局的tornado事件循環(huán),是服務(wù)器的引擎核心,示例中tornado.ioloop.IOLoop.current()就是默認(rèn)的tornado ioloop實(shí)例。
-2.app實(shí)例,它代表著一個(gè)完成的后端app,它會(huì)掛接一個(gè)服務(wù)端套接字端口對(duì)外提供服務(wù)。一個(gè)ioloop實(shí)例里面可以有多個(gè)app實(shí)例,示例中只有1個(gè),實(shí)際上可以允許多個(gè),不過(guò)一般幾乎不會(huì)使用多個(gè)。
-3.handler類(lèi),它代表著業(yè)務(wù)邏輯,我們進(jìn)行服務(wù)端開(kāi)發(fā)時(shí)就是編寫(xiě)一堆一堆的handler用來(lái)服務(wù)客戶(hù)端請(qǐng)求。
-4.路由表,它將指定的url規(guī)則和handler掛接起來(lái),形成一個(gè)路由映射表。當(dāng)請(qǐng)求到來(lái)時(shí),根據(jù)請(qǐng)求的訪(fǎng)問(wèn)url查詢(xún)路由映射表來(lái)找到相應(yīng)的業(yè)務(wù)handler。
這四大組件的關(guān)系是,一個(gè)ioloop包含多個(gè)app(管理多個(gè)服務(wù)端口),一個(gè)app包含一個(gè)路由表,一個(gè)路由表包含多個(gè)handler。ioloop是服務(wù)的引擎核心,它是發(fā)動(dòng)機(jī),負(fù)責(zé)接收和響應(yīng)客戶(hù)端請(qǐng)求,負(fù)責(zé)驅(qū)動(dòng)業(yè)務(wù)handler的運(yùn)行,負(fù)責(zé)服務(wù)器內(nèi)部定時(shí)任務(wù)的執(zhí)行。
當(dāng)一個(gè)請(qǐng)求到來(lái)時(shí),ioloop讀取這個(gè)請(qǐng)求解包成一個(gè)http請(qǐng)求對(duì)象,找到該套接字上對(duì)應(yīng)app的路由表,通過(guò)請(qǐng)求對(duì)象的url查詢(xún)路由表中掛接的handler,然后執(zhí)行handler。handler方法執(zhí)行后一般會(huì)返回一個(gè)對(duì)象,ioloop負(fù)責(zé)將對(duì)象包裝成http響應(yīng)對(duì)象序列化發(fā)送給客戶(hù)端。

Tornado的Redis使用
import json
import redis
import tornado.ioloop
import tornado.web
class FactorialService(object):
def __init__(self):
self.cache = redis.StrictRedis("localhost", 6379) # 緩存換成redis了
self.key = "factorials"
def calc(self, n):
s = self.cache.hget(self.key, str(n)) # 用hash結(jié)構(gòu)保存計(jì)算結(jié)果
if s:
return int(s), True
s = 1
for i in range(1, n):
s *= i
self.cache.hset(self.key, str(n), str(s)) # 保存結(jié)果
return s, False
classFactorialHandler(tornado.web.RequestHandler):
service = FactorialService()
def get(self):
n = int(self.get_argument("n") or 1) # 參數(shù)默認(rèn)值
fact, cached = self.service.calc(n)
result = {
"n": n,
"fact": fact,
"cached": cached
}
self.set_header("Content-Type", "application/json;charset=UTF-8")
self.write(json.dumps(result))
def make_app():
return tornado.web.Application([
(r"/fact", FactorialHandler),
])
if __name__ == "__main__":
app = make_app()
app.listen(8888)
tornado.ioloop.IOLoop.current().start()
當(dāng)我們?cè)俅卧L(fǎng)問(wèn)http://localhost:8888/fact?n=50,可以看到瀏覽器輸出如下
{"cached": false, "fact":608281864034267560872252163321295376887552831379210240000000000, "n":50}
,再刷新一下,瀏覽器輸出{"cached": true, "fact":608281864034267560872252163321295376887552831379210240000000000, "n":50},可以看到cached字段由true編程了false,表明緩存確實(shí)已經(jīng)保存了計(jì)算的結(jié)果。我們重啟一下進(jìn)程,
再次訪(fǎng)問(wèn)這個(gè)連接,觀(guān)察瀏覽器輸出,可以發(fā)現(xiàn)結(jié)果的cached依舊等于true。說(shuō)明緩存結(jié)果不再是存在本地內(nèi)存中了。
Tornado的項(xiàng)目結(jié)構(gòu):

備注:手工創(chuàng)建上述的文件夾及文件
-application.py
全局環(huán)境設(shè)置,包括靜態(tài)目錄設(shè)置、模板目錄設(shè)置、是否debug,及可能需要用到的cookies加密設(shè)置
-server.py
啟動(dòng)tornado服務(wù)主文件,主要包括web的訪(fǎng)問(wèn)URL及端口配置
url.py
URL路由,即用來(lái)配置訪(fǎng)問(wèn)某個(gè)URL路徑時(shí),該具體定位到哪個(gè)HTML文件
handles 文件夾
用來(lái)具體處理網(wǎng)頁(yè)動(dòng)態(tài)交互請(qǐng)求,里面主要是get和post方法中的邏輯開(kāi)發(fā)。
Handlers文件夾下的index.py
存放對(duì)應(yīng)的Handlers類(lèi)響應(yīng)請(qǐng)求
statics 文件夾
用來(lái)存放網(wǎng)頁(yè)需要的CSS樣式表文件、Javascript文件及圖片、視頻等靜態(tài)網(wǎng)頁(yè)內(nèi)容,這個(gè)文件夾的特征是,通過(guò)瀏覽器URL路徑可直接訪(fǎng)問(wèn),所以不要存放需要保密的后臺(tái)程序文件
templates 文件夾
用來(lái)存放靜態(tài)HTML文件。
1. tornado.web
tornado的基礎(chǔ)web框架模塊
RequestHandler
封裝了對(duì)應(yīng)一個(gè)請(qǐng)求的所有信息和方法,write(響應(yīng)信息)就是寫(xiě)響應(yīng)信息的一個(gè)方法;對(duì)應(yīng)每一種http請(qǐng)求方式(get、post等),把對(duì)應(yīng)的處理邏輯寫(xiě)進(jìn)同名的成員方法中(如對(duì)應(yīng)get請(qǐng)求方式,就將對(duì)應(yīng)的處理邏輯寫(xiě)在get()方法中),當(dāng)沒(méi)有對(duì)應(yīng)請(qǐng)求方式的成員方法時(shí),會(huì)返回“405: Method Not Allowed”錯(cuò)誤。
Application
Tornado Web框架的核心應(yīng)用類(lèi),是與服務(wù)器對(duì)接的接口,里面保存了路由信息表,其初始化接收的第一個(gè)參數(shù)就是一個(gè)路由信息映射元組的列表;其listen(端口)方法用來(lái)創(chuàng)建一個(gè)http服務(wù)器實(shí)例,并綁定到給定端口(注意:此時(shí)服務(wù)器并未開(kāi)啟監(jiān)聽(tīng))。
2. tornado.ioloop
tornado的核心io循環(huán)模塊,封裝了Linux的epoll和BSD的kqueue,tornado高性能的基石。以L(fǎng)inux的epoll為例,其原理如下圖:
IOLoop.current()
返回當(dāng)前線(xiàn)程的IOLoop實(shí)例。
IOLoop.start()
啟動(dòng)IOLoop實(shí)例的I/O循環(huán),同時(shí)服務(wù)器監(jiān)聽(tīng)被打開(kāi)。
總結(jié)Tornado Web程序編寫(xiě)思路
創(chuàng)建web應(yīng)用實(shí)例對(duì)象,第一個(gè)初始化參數(shù)為路由映射列表。
定義實(shí)現(xiàn)路由映射列表中的handler類(lèi)。
創(chuàng)建服務(wù)器實(shí)例,綁定服務(wù)器端口。
啟動(dòng)當(dāng)前線(xiàn)程的IOLoop。
3 httpserver
上一節(jié)我們說(shuō)在tornado.web.Application.listen()(示例代碼中的app.listen(8000))的方法中,創(chuàng)建了一個(gè)http服務(wù)器示例并綁定到給定端口
Tornado的常用操作:
-options
tornado.options模塊——全局參數(shù)定義、存儲(chǔ)、轉(zhuǎn)換。
tornado.options.define()
用來(lái)定義options選項(xiàng)變量的方法,定義的變量可以在全局的tornado.options.options中獲取使用,傳入?yún)?shù):
name 選項(xiàng)變量名,須保證全局唯一性,否則會(huì)報(bào)“Option'xxx' already defined in ...”的錯(cuò)誤;
default 選項(xiàng)變量的默認(rèn)值,如不傳默認(rèn)為None;
type 選項(xiàng)變量的類(lèi)型,從命令行或配置文件導(dǎo)入?yún)?shù)的時(shí)候tornado會(huì)根據(jù)這個(gè)類(lèi)型轉(zhuǎn)換輸入的值,轉(zhuǎn)換不成功時(shí)會(huì)報(bào)錯(cuò),可以是str、float、int、datetime、timedelta中的某個(gè),若未設(shè)置則根據(jù)default的值自動(dòng)推斷,若default也未設(shè)置,那么不再進(jìn)行轉(zhuǎn)換??梢酝ㄟ^(guò)利用設(shè)置type類(lèi)型字段來(lái)過(guò)濾不正確的輸入。
multiple 選項(xiàng)變量的值是否可以為多個(gè),布爾類(lèi)型,默認(rèn)值為False,如果multiple為True,那么設(shè)置選項(xiàng)變量時(shí)值與值之間用英文逗號(hào)分隔,而選項(xiàng)變量則是一個(gè)list列表(若默認(rèn)值和輸入均未設(shè)置,則為空列表[])。
help 選項(xiàng)變量的幫助提示信息,在命令行啟動(dòng)tornado時(shí),通過(guò)加入命令行參數(shù)--help 可以查看所有選項(xiàng)變量的信息(注意,代碼中需要加入tornado.options.parse_command_line())。
tornado.options.options
全局的options對(duì)象,所有定義的選項(xiàng)變量都會(huì)作為該對(duì)象的屬性。
tornado.options.parse_command_line()
轉(zhuǎn)換命令行參數(shù),并將轉(zhuǎn)換后的值對(duì)應(yīng)的設(shè)置到全局options對(duì)象相關(guān)屬性上。
-日志
當(dāng)我們?cè)诖a中調(diào)用parse_command_line()或者parse_config_file()的方法時(shí),tornado會(huì)默認(rèn)為我們配置標(biāo)準(zhǔn)logging模塊,即默認(rèn)開(kāi)啟了日志功能,并向標(biāo)準(zhǔn)輸出(屏幕)打印日志信息。
如果想關(guān)閉tornado默認(rèn)的日志功能,可以在命令行中添加--logging=none 或者在代碼中執(zhí)行如下操作:
from tornado.options import options, parse_command_line
options.logging= None
parse_command_line()
Tornado的常用模塊:
Web framework:
tornado.web — RequestHandler and Application classes
tornado.template - 模板
tornado.routing - 基本路由實(shí)現(xiàn)
tornado.escape - 轉(zhuǎn)義和字符串操作
tornado.locale - 國(guó)際化支持
tornado.websocket - 與瀏覽器的雙向通信socket
tornado的HTTP服務(wù)器和客戶(hù)端:
tornado.httpserver- 非阻塞HTTP服務(wù)器
tornado.httpclient - 異步HTTP客戶(hù)端
tornado.httputil - 操縱HTTP頭和URL
tornado.http1connection - HTTP / 1.x客戶(hù)端/服務(wù)器實(shí)現(xiàn)
異步網(wǎng)絡(luò):
tornado.ioloop -主事件循環(huán)
tornado.iostream - Flexible output generation(靈活的輸出生成)
tornado.netutil - 操作HTTP報(bào)頭和URL的工具類(lèi)
tornado.tcpclient- IOStream連接工廠(chǎng)
tornado.tcpserver- IOStream基于TCP的服務(wù)器
協(xié)程和并發(fā)
tornado.gen - 基于發(fā)生器的協(xié)程
tornado.locks - 同步原語(yǔ)
tornado.queues - 協(xié)程隊(duì)列
tornado.process - 用于多個(gè)進(jìn)程的實(shí)用程序
與其他服務(wù)集成
tornado.auth - 使用OpenID和OAuth進(jìn)行第三方登錄
tornado.wsgi - 與其他Python框架和Web服務(wù)器的網(wǎng)關(guān)接口
tornado.platform.caresresolver - 使用C-Ares的異步DNS解析器
tornado.platform.twisted - twisted和tornado之間的橋梁
tornado.platform.asyncio- asyncio和tornado之間的橋梁
Utilities
tornado.autoreload - 自動(dòng)檢測(cè)開(kāi)發(fā)中的代碼更改
tornado.concurrent- Work withFuture objects
tornado.log - 打印日志的
tornado.options - 命令行解析
tornado.stack_context - 跨異步回調(diào)的異常處理
tornado.testing - 對(duì)異步代碼的單元測(cè)試支持
tornado.util - 通用工具類(lèi)
參考鏈接:
Tornado簡(jiǎn)介:
https://blog.csdn.net/Ka_Ka314/article/details/81163740
Tornado的項(xiàng)目架構(gòu):
https://zhuanlan.zhihu.com/p/127922316
Tornado+Redis
https://zhuanlan.zhihu.com/p/37382503
Tornado簡(jiǎn)易教程
https://blog.csdn.net/belalds/article/details/80575755
Tornado開(kāi)發(fā)教程
https://www.cnblogs.com/jiangxiaobo/p/12776283.html
用 gunicorn + gevent 跑 tornado app
https://zhuanlan.zhihu.com/p/31635068
Tornado手冊(cè)
http://www.ttlsa.com/docs/tornado/#overview
Tornado官網(wǎng)
https://www.tornadoweb.org/en/stable/
Tornado的前后端
https://www.cnblogs.com/xiaobeibei26/p/6646083.html
公眾號(hào)二維碼
End:如果有興趣了解金融量化交易和其他數(shù)據(jù)分析的實(shí)用技術(shù),歡迎關(guān)注本公眾號(hào)
