簡(jiǎn)介
Tornado龍卷風(fēng)是一個(gè)開源的網(wǎng)絡(luò)服務(wù)器框架,它是基于社交聚合網(wǎng)站FriendFeed的實(shí)時(shí)信息服務(wù)開發(fā)而來(lái)的。2007年由4名Google前軟件工程師一起創(chuàng)辦了FriendFeed杨箭,旨在使用戶能夠方便地跟蹤好友在Facebook和Twitter等多個(gè)社交網(wǎng)站上的活動(dòng)盒让。結(jié)果兩年后,F(xiàn)acebook宣布收購(gòu)FriendFeed伴郁,交易價(jià)格約為5000萬(wàn)美元。而此時(shí)FriendFeed只有12名員工蛋叼。據(jù)說(shuō)這幫人后來(lái)又到了Google焊傅,搞出了現(xiàn)在的Google App Engine...
我們開發(fā)這個(gè)Web服務(wù)器的主要目的就是為了處理FriendFeed的實(shí)時(shí)功能 -- 在FriendFeed的應(yīng)用里每個(gè)活動(dòng)用戶都會(huì)保持著一個(gè)服務(wù)器連接剂陡。
Tornado使FriendFeed使用的可擴(kuò)展的非阻塞Web服務(wù)器及其相關(guān)工具的開源版本,這個(gè)Web框架看起來(lái)有些像web.py或 Google的webapp狐胎,不過(guò)為了更加有效地利用非阻塞服務(wù)器環(huán)境鸭栖,Tornado這個(gè)Web框架還包含了一些相關(guān)的有用工具和優(yōu)化。
區(qū)別
Tornado與現(xiàn)代主流的Web服務(wù)器框架有著明顯的區(qū)別:它使非阻塞式的服務(wù)器握巢,速度相當(dāng)快晕鹊。這得益于其非阻塞的方式和對(duì)epoll的運(yùn)用。Tornado每秒可以處理數(shù)以千計(jì)的連接暴浦,對(duì)于實(shí)時(shí)Web服務(wù)來(lái)說(shuō)Tornado確實(shí)是一個(gè)理想的Web框架溅话。
與Node.js相同的是,Tornado也采用的是單進(jìn)程單線程異步IO的網(wǎng)絡(luò)模型歌焦,它們都可以編寫異步非阻塞的程序飞几。但由于Node.js是Google Chrome V8引擎的JS運(yùn)行環(huán)境或工具包,它屬于偏底層的抽象独撇,擴(kuò)展了JS編寫服務(wù)器程序的能力屑墨,所以基于Node.js會(huì)由不同的Web框架。從這個(gè)角度來(lái)看Node.js和Tornado其實(shí)并不在一個(gè)層次上纷铣。
Tornado是使用Python編寫的Web服務(wù)器兼Web應(yīng)用框架卵史,與主流Web服務(wù)器框架不同的是,Tornado是異步非阻塞式服務(wù)器搜立,得益于非阻塞式和對(duì)epoll
模型的運(yùn)用以躯,Tornado是實(shí)時(shí)Web服務(wù)的一個(gè)理想框架,它非常適合開發(fā)長(zhǎng)輪詢儒拂、WebSocket和需要與每個(gè)用戶建立持久連接的應(yīng)用寸潦。
特點(diǎn)
- 輕量級(jí)Web框架
- 異步非阻塞IO處理方式
Tornado采用的單進(jìn)程單線程異步IO的網(wǎng)絡(luò)模式,其高性能源于Tornado基于Linux的Epoll(UNIX為kqueue)的異步網(wǎng)絡(luò)IO社痛。 - 出色的抗負(fù)載能力
- 不依賴多進(jìn)程或多線程
- WSGI全棧替代產(chǎn)品
WSGI把應(yīng)用(Application)和服務(wù)器(Server)結(jié)合起來(lái)见转,Tornado既可以是WSGI應(yīng)用也可以是WSGI服務(wù)。 - 既是WebServer也是WebFramework
Tornado是基于Bret Taylor和其他人員為FrientFeed所開發(fā)的網(wǎng)絡(luò)服務(wù)框架蒜哀,當(dāng)FriendFeed被Facebook收購(gòu)后得以開源斩箫。不同于那些最多只能達(dá)到1w并發(fā)連接的傳統(tǒng)網(wǎng)絡(luò)服務(wù)器。Tornado在設(shè)計(jì)之初就考慮到了性能因素撵儿,旨在解決C10K問(wèn)題乘客,這樣的設(shè)計(jì)使其成為一個(gè)擁有高性能的框架。
結(jié)構(gòu)
- Web框架
主要包括RequestHandler
用于創(chuàng)建Web應(yīng)用程序和各種支持類的子類 - HTTP服務(wù)器與客戶端
主要包括HTTPServer
和AsyncHTTPClient
- 異步網(wǎng)絡(luò)庫(kù)
主要包括IOLoop
和IOStream
作為HTTP組件的構(gòu)建塊 - 協(xié)程庫(kù)
Tornado的Web框架和HTTP服務(wù)器一起提供了完整的堆棧替代方案WSGI
模塊
Tornado是一個(gè)輕量級(jí)框架淀歇,它的模塊不多最重要的模塊是web易核,web模塊包含了Tornado大部分主要功能的Web框架,其他模塊都是工具性質(zhì)的浪默,以便讓W(xué)eb模塊更加有用牡直。
- Core Web Framework 核心Web框架
-
tornado.web
包括Web框架大部分主要功能缀匕,包括RequestHandler
和Application
類。 -
tornado.httpserver
一個(gè)無(wú)阻塞HTTP服務(wù)器的實(shí)現(xiàn) -
tornado.template
模板系統(tǒng) -
tornado.escape
HTML碰逸、JSON乡小、URLs等編碼解碼和字符串操作 -
tornado.locale
國(guó)際化支持
- Asynchronous Networking 異步網(wǎng)絡(luò)底層模塊
-
tornado.ioloop
核心IO循環(huán) -
tornado.iostream
對(duì)非阻塞的Socket的簡(jiǎn)單封裝以方便常用讀寫操作 -
tornado.httpclient
無(wú)阻塞的HTTP服務(wù)器實(shí)現(xiàn) -
tornado.netutil
網(wǎng)絡(luò)應(yīng)用的實(shí)現(xiàn)主要是TCPServer類
- Integration With Other Services 系統(tǒng)集成服務(wù)
-
tornado.auth
使用OpenId和OAuth進(jìn)行第三方登錄 -
tornado.database
MySQL服務(wù)端封裝 -
tornado.platform.twisted
在Tornado上運(yùn)行Twisted實(shí)現(xiàn)的代碼 -
tornado.websocket
實(shí)現(xiàn)和瀏覽器的雙向通信 -
tornado.wsgi
其他Python網(wǎng)絡(luò)框架或服務(wù)器的相互操作
- Utilities 應(yīng)用模塊
-
tornado.autoload
產(chǎn)生環(huán)境中自動(dòng)檢查代碼更新 -
tornado.gen
基于生成器的接口,使用該模塊 保證代碼異步運(yùn)行。 -
tornado.httputil
分析HTTP請(qǐng)求內(nèi)容 -
tornado.options
解析終端參數(shù) -
tornado.process
多進(jìn)程實(shí)現(xiàn)的封裝 -
tornado.stack_context
異步環(huán)境中對(duì)回調(diào)函數(shù)上下文保存、異常處理 -
tornado.testing
單元測(cè)試
Tornado服務(wù)器的三個(gè)底層核心模塊
-
httpserver
服務(wù)于web模塊的一個(gè)簡(jiǎn)單的HTTP服務(wù)器的實(shí)現(xiàn)
Tornado的HTTPConnection
類用來(lái)處理HTTP請(qǐng)求仗处,包括讀取HTTP請(qǐng)求頭、讀取POST傳遞的數(shù)據(jù)湃番,調(diào)用用戶自定義的處理方法,以及把響應(yīng)數(shù)據(jù)寫給客戶端的socket
厌蔽。 -
iostream
對(duì)非阻塞式的socket
的封裝以便于常見讀寫操作
為了在處理請(qǐng)求時(shí)實(shí)現(xiàn)對(duì)socket
的異步讀寫牵辣,Tornado實(shí)現(xiàn)了IOStream
類用來(lái)處理socket
的異步讀寫。 -
ioloop
核心的I/O循環(huán)
Tornado為了實(shí)現(xiàn)高并發(fā)和高性能奴饮,使用了一個(gè)IOLoop
事件循環(huán)來(lái)處理socket
的讀寫事件,IOLoop
事件循環(huán)是基于Linux的epoll
模型择浊,可以高效地響應(yīng)網(wǎng)絡(luò)事件戴卜,這是Tornado高效的基礎(chǔ)保證。
設(shè)計(jì)模型
Tornado不僅僅是一個(gè)Web框架琢岩,它完整地實(shí)現(xiàn)了HTTP服務(wù)器和客戶端投剥,再此基礎(chǔ)上提供了Web服務(wù),它可分為四層:
- Web框架:最上層担孔,包括處理器江锨、模板、數(shù)據(jù)庫(kù)連接糕篇、認(rèn)證啄育、本地化等Web框架所需功能。
- HTTP/HTTPS層:基于HTTP協(xié)議實(shí)現(xiàn)了HTTP服務(wù)器和客戶端
- TCP層:實(shí)現(xiàn)TCP服務(wù)器負(fù)責(zé)數(shù)據(jù)傳輸
- Event層:最底層拌消、處理IO事件
使用Tornado可以方便地架構(gòu)出各種類型的web服務(wù)器挑豌,以HTTP服務(wù)器為例來(lái)分析下web服務(wù)器的工作方式。
一個(gè)請(qǐng)求處理的處理過(guò)程
- 服務(wù)器綁定
bind
到特定端口port
墩崩,然后開始監(jiān)聽listen
客戶端的請(qǐng)求氓英。 - 當(dāng)客戶端連接
connect
到來(lái)時(shí),會(huì)將請(qǐng)求發(fā)送給服務(wù)器鹦筹。 - 服務(wù)器處理請(qǐng)求完畢后返回響應(yīng)結(jié)果給客戶端
當(dāng)需要處理成千上萬(wàn)的連接的時(shí)候铝阐,就會(huì)遇到典型的The C10K Program
問(wèn)題,常見的解決方案有
- 一個(gè)線程服務(wù)多個(gè)客戶端铐拐,使用非阻塞I/O和水平觸發(fā)的就緒通知徘键。
- 一個(gè)線程服務(wù)多個(gè)客戶端练对,使用非阻塞I/O和就緒改變時(shí)通知。
- 一個(gè)服務(wù)線程服務(wù)于多個(gè)客戶端啊鸭,使用異步I/O锹淌。
- 一個(gè)服務(wù)線程服務(wù)于一個(gè)客戶端,使用阻塞I/O赠制。
- 將服務(wù)代碼編譯進(jìn)內(nèi)核
Tornado采用的方式是“多進(jìn)程 + 非阻塞 + epoll模式”
安裝配置
參考資料
- GitHub主頁(yè) https://github.com/tornadoweb/tornado
- 官方網(wǎng)站 http://www.tornadoweb.org/en/stable/
- Tornado4.3 https://tornado-zh.readthedocs.io/zh/latest/
- Tornado6.0 https://www.osgeo.cn/tornado/index.html
- Python下載列表 https://www.python.org/downloads/
版本問(wèn)題
- Tornado4.3 可以運(yùn)行在Python2.6赂摆、2.7、3.2+
- Tornado6.0需要Python3.5.2+
CentOS安裝Tornado
環(huán)境檢查
# 查看python版本
$ python --version
Python 2.7.5
$ python
Python 2.7.5 (default, Apr 9 2019, 14:30:50)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-36)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>
CentOS安裝Python3.7并保留Python2
# 安裝依賴
$ yum -y install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-devel xz-devel libffi-devel gcc gcc-c++
# 創(chuàng)建目錄
$ mkdir /usr/local/python3
# 下載編譯安裝
$ wget https://www.python.org/ftp/python/3.7.3/Python-3.7.3.tgz
$ tar -zxvf Python3.7.3.tgz && cd Python3.7.3
$ ./configure
$ make && make install
# 創(chuàng)建軟連接
$ ln -s /usr/local/python3/bin/python3 /usr/bin/python3
$ ln -s /usr/local/python3/bin/pip3 /usr/bin/pip3
# 測(cè)試版本
$ python3 -V
Python 3.7.3
$ pip3 -v
pip 19.0.3 from /usr/local/lib/python3.7/site-packages/pip (python 3.7)
# 使用pip3安裝tornado
$ pip3 install tornado
Collecting tornado
Using cached https://files.pythonhosted.org/packages/30/78/2d2823598496127b21423baffaa186b668f73cd91887fcef78b6eade136b/tornado-6.0.3.tar.gz
Installing collected packages: tornado
Running setup.py install for tornado ... done
Successfully installed tornado-6.0.3
You are using pip version 19.0.3, however version 19.1.1 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.
# 測(cè)試tornado
$ python3
Python 3.7.3 (default, Jul 8 2019, 17:00:47)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-36)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import tornado
CentOS安裝配置pip
# CentOS安裝EPEL擴(kuò)展源
$ yum install -y epel-release
# CentOS安裝Python Pip
$ yum install -y python-pip
# 查看pip版本
$ pip --version
pip 19.0.3 from /usr/local/lib/python3.7/site-packages/pip (python 3.7)
# 更新pip
$ pip install --upgrade pip
$ pip --version
pip 19.0.3 from /usr/local/lib/python3.7/site-packages/pip (python 3.7)
可直接使用git clone
從GitHub中克隆下載Tornado源碼钟些,也可以使用pip
命令安裝Tornado烟号。
# 使用GitHub克隆源碼包
$ git clone https://github.com/tornadoweb/tornado
$ cd tornado
$ python setup.py install
檢查當(dāng)前Python環(huán)境
$ python -V
Python 3.7.3
$ pip -V
pip 19.0.3 from /usr/local/lib/python3.7/site-packages/pip (python 3.7)
當(dāng)前 Tornado 版本為 6.0.3,Tornado6.0開始將拋棄對(duì)Python2.7和3.4的支持政恍,同時(shí)將Python3.5.2作為最低支持版本汪拥。
Ubuntu安裝Tornado
Ubuntu自身安裝了Python2和Python3,默認(rèn)采用的是Python3篙耗,使用時(shí)需要進(jìn)行切換版本迫筑。
檢查當(dāng)前系統(tǒng)環(huán)境
$ uname -a
# 檢查內(nèi)核版本
$ uname -r
檢查當(dāng)前Python環(huán)境
$ python -V
Python 2.7.15+
$ whereis python
python: /usr/bin/python2.7 /usr/bin/python2.7-config /usr/bin/python3.6 /usr/bin/python /usr/bin/python3.6m /usr/lib/python2.7 /usr/lib/python3.6 /usr/lib/python3.7 /etc/python2.7 /etc/python3.6 /etc/python /usr/local/lib/python2.7 /usr/local/lib/python3.6 /usr/include/python2.7 /usr/include/python3.6m /usr/share/python /usr/share/man/man1/python.1.gz
$ pip -V
pip 9.0.1 from /usr/lib/python2.7/dist-packages (python 2.7)
$ whereis pip
pip: /usr/bin/pip /usr/share/man/man1/pip.1.gz
$ type pip
pip 已被錄入哈希表 (/usr/bin/pip)
$ python2 -V
Python 2.7.15+
python2: /usr/bin/python2.7 /usr/bin/python2.7-config /usr/bin/python2 /usr/lib/python2.7 /etc/python2.7 /usr/local/lib/python2.7 /usr/include/python2.7 /usr/share/man/man1/python2.1.gz
$ python3 -V
Python 3.6.8
$ whereis python3
python3: /usr/bin/python3 /usr/bin/python3.6 /usr/bin/python3.6m /usr/lib/python3 /usr/lib/python3.6 /usr/lib/python3.7 /etc/python3 /etc/python3.6 /usr/local/lib/python3.6 /usr/include/python3.6m /usr/share/python3 /usr/share/man/man1/python3.1.gz
安裝Python3
$ sudo apt-get update
$ sudo apt-get install python3
調(diào)整Python3的優(yōu)先級(jí)
$ sudo update-alternative --install /usr/bin/python3 python3 /usr/bin/python3.6 1
調(diào)整Python版本的默認(rèn)值為Python3
$ sudo update-alternatives --install /usr/bin/python python /usr/bin/python3 100
$ sudo update-alternatives --install /usr/bin/python python /usr/bin/python3 150
$ python -V
Python 3.6.8
安裝pip
$ sudo apt install python-pip
$ sudo apt install python3-pip
$ pip -V
pip 9.0.1 from /usr/lib/python3/dist-packages (python 3.6)
$ pip2 -V
pip 19.1.1 from /usr/local/lib/python2.7/dist-packages/pip (python 2.7)
$ pip3 -V
pip 9.0.1 from /usr/lib/python3/dist-packages (python 3.6)
$ whereis pip3
pip3: /usr/bin/pip3 /usr/share/man/man1/pip3.1.gz
更新pip為最新版
$ sudo pip install --upgrade pip
$ pip -V
Traceback (most recent call last):
File "/usr/bin/pip", line 9, in <module>
from pip import main
ModuleNotFoundError: No module named 'pip'
$ sudo pip -V
# pip版本回退
$ sudo python3 -m pip install --user --upgrade pip==9.0.1
卸載pip
$ sudo apt-get remove python-pip
$ sudo apt-get remove python2-pip
$ duso apt-get remove python3-pip
查看pip已安裝列表
$ pip list
安裝Tornado
$ pip install tornado
Successfully installed tornado-6.0.3
$ python
>>> import tornado
文件結(jié)構(gòu)
開發(fā)流程
首先創(chuàng)建Web應(yīng)用程序Application
,并將指定處理器Handler
傳遞過(guò)去宗弯,然后開始監(jiān)聽指定端口脯燃,最后啟動(dòng)事件循環(huán),并開始監(jiān)聽網(wǎng)絡(luò)事件蒙保,主要是socket
的讀寫操作辕棚。
創(chuàng)建腳本
$ vim server.py
#! /usr/bin/python
# encoding:utf-8
# 導(dǎo)入Tornado模塊
import tornado.ioloop #核心IO循環(huán)模塊
import tornado.httpserver #異步非阻塞HTTP服務(wù)器模塊
import tornado.web #Web框架模塊
import tornado.options #解析終端參數(shù)模塊
#從終端模塊中導(dǎo)出define模塊用于讀取參數(shù),導(dǎo)出options模塊用于設(shè)置默認(rèn)參數(shù)
from tornado.options import define, options
# 定義端口用于指定HTTP服務(wù)監(jiān)聽的端口
# 如果命令行中帶有port同名參數(shù)則會(huì)稱為全局tornado.options的屬性邓厕,若沒(méi)有則使用define定義逝嚎。
define("port", type=int, default=8000, help="run on the given port")
# 創(chuàng)建請(qǐng)求處理器
# 當(dāng)處理請(qǐng)求時(shí)會(huì)進(jìn)行實(shí)例化并調(diào)用HTTP請(qǐng)求對(duì)應(yīng)的方法
class IndexHandler(tornado.web.RequestHandler):
# 定義get方法對(duì)HTTP的GET請(qǐng)求做出響應(yīng)
def get(self):
# 從querystring查詢字符串中獲取id參數(shù)的值,若無(wú)則默認(rèn)為0.
id = self.get_argument("id", 0)
# write方法將字符串寫入HTTP響應(yīng)
self.write("hello world id = " + id)
# 創(chuàng)建路由表
urls = [(r"/", IndexHandler),]
# 定義服務(wù)器
def main():
# 解析命令行參數(shù)
tornado.options.parse_command_line()
# 創(chuàng)建應(yīng)用實(shí)例
app = tornado.web.Application(urls)
# 監(jiān)聽端口
app.listen(options.port)
# 創(chuàng)建IOLoop實(shí)例并啟動(dòng)
tornado.ioloop.IOLoop.current().start()
# 應(yīng)用運(yùn)行入口详恼,解析命令行參數(shù)
if __name__ == "__main__":
# 啟動(dòng)服務(wù)器
main()
運(yùn)行服務(wù)器
$ python server.py
運(yùn)行測(cè)試
$ curl http://127.0.0.1:8000
進(jìn)程監(jiān)控
SuperVisor是一個(gè)進(jìn)程監(jiān)控程序补君,當(dāng)進(jìn)程需要不間斷運(yùn)行時(shí)由于各種原因可能中斷,當(dāng)進(jìn)程中斷時(shí)希望能夠自動(dòng)重啟单雾,此時(shí)就可以使用SuperVisor赚哗。
# 安裝
$ pip install supervisor
# 配置
$ echo_supervisord_conf > /etc/supervisord.conf
# 開啟配置
$ vim /etc/supervisord.conf
;[include]
files = /etc/supervisor/*.ini
# 創(chuàng)建配置
$ mkdir /etc/supervisor
$ cd /etc/supervisor
$ vim tornado.ini
[program:tornado]
command=python3 /home/tornado/server.py --port=8000
directory=/home/tornado/
autorestart=true
redirect_stderr=true
# 重啟進(jìn)程
$ ps aux | grep supervisord
$ supervisord -c /etc/supervisor.conf
調(diào)試模式
$ vim server.py
應(yīng)用程序執(zhí)行后會(huì)首先解析并選擇參數(shù),然后創(chuàng)建有一個(gè)Application
實(shí)例并傳遞給HTTPServer實(shí)例并啟動(dòng)硅堆。到此HTTPServer啟動(dòng)屿储,tornado.httpserver
模塊用來(lái)支持非阻塞的HTTPServer。啟動(dòng)服務(wù)器后還需啟動(dòng)IOLoop實(shí)例以啟動(dòng)事件循環(huán)機(jī)制渐逃,配合非阻塞HTTPServer一起工作够掠。
代碼組織結(jié)構(gòu)
注釋
import 語(yǔ)句
定義選項(xiàng)參數(shù)
Application定義
BaseHandler定義
xxxHandler定義
main()定義
#! /usr/bin/python
# encoding:utf-8
from tornado.ioloop import IOLoop
from tornado.httpserver import HTTPServer
from tornado.web import Application, RequestHandler, url
#從終端模塊中導(dǎo)出define模塊用于讀取參數(shù),導(dǎo)出options模塊用于設(shè)置默認(rèn)參數(shù)
from tornado.options import define, options
#開始調(diào)試模式
import tornado.autoreload
# 定義端口用于指定HTTP服務(wù)監(jiān)聽的端口
# 如果命令行中帶有port同名參數(shù)則會(huì)稱為全局tornado.options的屬性茄菊,若沒(méi)有則使用define定義疯潭。
define("port", type=int, default=8000, help="run on the given port")
# 調(diào)試模式
define("debug", type=bool, default=True, help="debug mode")
# 創(chuàng)建請(qǐng)求處理器
# 當(dāng)處理請(qǐng)求時(shí)會(huì)進(jìn)行實(shí)例化并調(diào)用HTTP請(qǐng)求對(duì)應(yīng)的方法
class MainHandler(RequestHandler):
# 定義get方法對(duì)HTTP的GET請(qǐng)求做出響應(yīng)
def get(self, *args, **kwargs):
# 從querystring查詢字符串中獲取id參數(shù)的值赊堪,若無(wú)則默認(rèn)為0.
id = self.get_query_argument("id", strip=True)
# write方法將字符串寫入HTTP響應(yīng)
self.write("hello world id = " + str(id))
# 創(chuàng)建路由表
urls = [
(r"/", MainHandler),
(r"/index", MainHandler)
]
# 創(chuàng)建配置
settings = dict(
debug = options.debug
)
# 創(chuàng)建應(yīng)用
def make_app():
return Application(urls, settings)
# 定義服務(wù)器
def main():
# 解析命令行參數(shù)
options.parse_command_line()
# 創(chuàng)建應(yīng)用
app = make_app()
# 創(chuàng)建HTTP服務(wù)器實(shí)例
server = HTTPServer(app)
# 監(jiān)聽端口
server.listen(options.port)
# 創(chuàng)建IOLoop實(shí)例并啟動(dòng)
IOLoop.current().start()
# 應(yīng)用運(yùn)行入口,解析命令行參數(shù)
if __name__ == "__main__":
# 啟動(dòng)服務(wù)器
main()
運(yùn)行測(cè)試
$ python server.py
瀏覽器訪問(wèn)http://127.0.0.1:8000?id=1000
查看服務(wù)器輸出
定義選項(xiàng)參數(shù)
Tornado提供了tornado.options.define
方法用來(lái)簡(jiǎn)化選項(xiàng)參數(shù)的定義竖哩。
# 定義端口
define("port", type=int, default=8000, help="run on the given port")
# 定義調(diào)試模式
define("debug", type=bool, default=True, help="debug mode")
解析命令行參數(shù)
tornado.options.parse_command_line()
創(chuàng)建Web應(yīng)用實(shí)例
app = tornado.web.Application(urls, settings)
Tornado中Application
應(yīng)用類是Handler處理器的集合
Application
類的__init__
初始化函數(shù)原型
# 原型
def __init__(self, handlers=None, default_host="", transforms=None, wsgi=False, **settings):
Tornado的HTTPServer
會(huì)負(fù)責(zé)解析用戶的HTTPRequest
哭廉,構(gòu)造一個(gè)request
對(duì)象。并交給RequestHandler
處理相叁,Request
的解析是一個(gè)規(guī)劃化的流程遵绰,針對(duì)Request
的處理函數(shù)RequestHandler
是被自定義的重點(diǎn)部分。
由于HTTP是工作在TCP協(xié)議之上的增淹,HTTPServer
其實(shí)是TCPServer
的派生類椿访,常規(guī)socket
編程中啟動(dòng)一個(gè)TCPServer有三個(gè)必備步驟:
- 創(chuàng)建
socket
- 綁定指定地址的端口
- 執(zhí)行監(jiān)聽
TCPServer類的實(shí)現(xiàn)借鑒UNIX/Linux中的Socket機(jī)制,也必然存在上述步驟虑润,這幾個(gè)步驟都是在HTTPServer.listen()
函數(shù)調(diào)用時(shí)完成的成玫。
server.listen(options.port)
listen
函數(shù)的參數(shù)是端口號(hào),端口定義可通過(guò)define
來(lái)定義拳喻。
from tornado.options import define, options
define("port", default=8888, help="run on the given port", type=int)
define
函數(shù)是OptionParser
類的成員哭当,定義在tornado/options.py
文件中,機(jī)制于parse_command_line()
類似冗澈。define
定義端口port
或荣病,port
變量會(huì)被存放在options
對(duì)象的directory
成員中,因此可直接使用options.port
訪問(wèn)渗柿。
當(dāng)使用server.listen(options.port)
后,服務(wù)器就會(huì)在端口上啟動(dòng)一個(gè)服務(wù)脖岛,并開始監(jiān)聽客戶端的連接朵栖。對(duì)于常規(guī)的Socket操作,listen
之后的操作應(yīng)該是accept
柴梆。
在Tornado中accept
操作是這樣的:
tornado.ioloop.IOLoop.current().start()
IOLoop是什么呢陨溅?IOLoop于TCPServer之間的關(guān)系其實(shí)很簡(jiǎn)單。例如使用C語(yǔ)言編寫TCP服務(wù)器時(shí)绍在,編寫完create-bind-listen
三段式之后门扇,都需要編寫accept/recv/send
處理客戶端請(qǐng)求。通常會(huì)寫一個(gè)無(wú)限循環(huán)偿渡,不斷調(diào)用accept
來(lái)響應(yīng)客戶端連接臼寄,其實(shí)這個(gè)無(wú)線循環(huán)就是Tornado中的IOLoop。
IOLoop會(huì)負(fù)責(zé)accept
這一步溜宽,對(duì)于recv/send
操作通常也是在一個(gè)循環(huán)中進(jìn)行的吉拳,也可以抽象成IOLoop。
最后适揉,簡(jiǎn)單梳理下整個(gè)流程:當(dāng)我們使用在客戶端瀏覽器地址欄中輸入http://127.0.0.1:8000?id=1000
時(shí)留攒,瀏覽器首先會(huì)連接服務(wù)器 煤惩,將HTTP請(qǐng)求發(fā)送到HTTPServer中,HTTPServer會(huì)先解析請(qǐng)求parse request
炼邀,然后將請(qǐng)求request
交給第一個(gè)匹配到的處理器Handler
魄揉。處理器Handler
會(huì)負(fù)責(zé)組織數(shù)據(jù)并調(diào)用發(fā)送API將數(shù)據(jù)發(fā)送到客戶端。
核心組件
Tornado的Web服務(wù)器通常包含四大組件
ioloop
實(shí)例
tornado.ioloop
是全局Tornado的IO事件循環(huán)拭宁,是服務(wù)器的引擎核心洛退。
tornado.ioloop
是核心IO循環(huán)模塊,封裝了Linux的epoll
和BSD的kqueue
红淡,是Tornado高性能處理的核心不狮。
tornado.ioloop.IOLoop.current()
返回當(dāng)前線程的IOLoop
實(shí)例對(duì)象
tornado.ioloop.IOLoop.current().start()
用于啟動(dòng)IOLoop
實(shí)例對(duì)象的IO循環(huán)并開啟監(jiān)聽
# 加載Tornado核心IO事件循環(huán)模塊
import tornado.ioloop
# 默認(rèn)Tornado的ioloop實(shí)例
tornado.ioloop.IOLoop.current()
app
實(shí)例
app
實(shí)例代表了一個(gè)完成的后端應(yīng)用,它會(huì)掛接一個(gè)服務(wù)端套接字端口并對(duì)外提供服務(wù)在旱,一個(gè)ioloop
事件循環(huán)實(shí)例中可以包含多個(gè)app
實(shí)例摇零。
# 創(chuàng)建應(yīng)用實(shí)例
app = tornado.web.Application(urls)
# 監(jiān)聽端口
app.listen(options.port)
urls
路由表
路由表用于將指定URL規(guī)則和處理器Handler掛接起來(lái)形成路由映射表,當(dāng)請(qǐng)求到來(lái)時(shí)會(huì)根據(jù)請(qǐng)求的訪問(wèn)URL查詢路由映射表來(lái)查詢對(duì)應(yīng)業(yè)務(wù)的處理器Handler
桶蝎。
urls = [(r"/", MainHandler),]
handler
類
handler
類代表著業(yè)務(wù)邏輯驻仅,在進(jìn)行服務(wù)端開發(fā)時(shí)也就是在編寫處理器,用以服務(wù)客戶端請(qǐng)求登渣。
# 當(dāng)處理請(qǐng)求時(shí)會(huì)進(jìn)行實(shí)例化并調(diào)用HTTP請(qǐng)求對(duì)應(yīng)的方法
class MainHandler(tornado.web.RequestHandler):
# 定義get方法對(duì)HTTP的GET請(qǐng)求做出響應(yīng)
def get(self):
# 從querystring查詢字符串中獲取id參數(shù)的值噪服,若無(wú)則默認(rèn)為0.
id = self.get_argument("id", 0)
# write方法將字符串寫入HTTP響應(yīng)
self.write("hello world id = " + id)
四大組件的關(guān)系
- 一個(gè)IO事件循環(huán)
ioloop
可以包含多個(gè)應(yīng)用app
,即可以管理多個(gè)服務(wù)端口胜茧。 - 一個(gè)應(yīng)用
app
可以包含一個(gè)路由表urls
- 一個(gè)路由表
urls
可以包含多個(gè)處理器Handler
ioloop
是服務(wù)的引擎核心是發(fā)動(dòng)機(jī)粘优,負(fù)責(zé)接收和響應(yīng)客戶端請(qǐng)求,負(fù)責(zé)驅(qū)動(dòng)業(yè)務(wù)處理器handler
的運(yùn)行呻顽,負(fù)責(zé)服務(wù)器內(nèi)部定時(shí)任務(wù)的執(zhí)行雹顺。同一個(gè)ioloop
實(shí)例會(huì)運(yùn)行在一個(gè)單線程環(huán)境下。
當(dāng)一個(gè)請(qǐng)求到來(lái)時(shí)廊遍,IO事件循環(huán)ioloop
會(huì)讀取請(qǐng)求并解包形成 一個(gè)HTTP請(qǐng)求對(duì)象嬉愧,并找到該套接字上對(duì)應(yīng)應(yīng)用app
的路由表urls
,通過(guò)請(qǐng)求對(duì)象的URL查詢路由表中掛接的處理器Handler
喉前,然后執(zhí)行處理器Handler
没酣。handler
處理器執(zhí)行后會(huì)返回一個(gè)對(duì)象,ioloop
負(fù)責(zé)將對(duì)象包裝成HTTP響應(yīng)對(duì)象并序列化發(fā)送給客戶端卵迂。
未完待續(xù)...