Tornado

龍卷風(fēng)

簡(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ù)器與客戶端
    主要包括HTTPServerAsyncHTTPClient
  • 異步網(wǎng)絡(luò)庫(kù)
    主要包括IOLoopIOStream作為HTTP組件的構(gòu)建塊
  • 協(xié)程庫(kù)

Tornado的Web框架和HTTP服務(wù)器一起提供了完整的堆棧替代方案WSGI

模塊

Tornado是一個(gè)輕量級(jí)框架淀歇,它的模塊不多最重要的模塊是web易核,web模塊包含了Tornado大部分主要功能的Web框架,其他模塊都是工具性質(zhì)的浪默,以便讓W(xué)eb模塊更加有用牡直。

  1. Core Web Framework 核心Web框架
  • tornado.web 包括Web框架大部分主要功能缀匕,包括RequestHandlerApplication類。
  • tornado.httpserver一個(gè)無(wú)阻塞HTTP服務(wù)器的實(shí)現(xiàn)
  • tornado.template模板系統(tǒng)
  • tornado.escape HTML碰逸、JSON乡小、URLs等編碼解碼和字符串操作
  • tornado.locale國(guó)際化支持
  1. 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類
  1. Integration With Other Services 系統(tǒng)集成服務(wù)
  • tornado.auth 使用OpenId和OAuth進(jìn)行第三方登錄
  • tornado.databaseMySQL服務(wù)端封裝
  • tornado.platform.twisted在Tornado上運(yùn)行Twisted實(shí)現(xiàn)的代碼
  • tornado.websocket實(shí)現(xiàn)和瀏覽器的雙向通信
  • tornado.wsgi其他Python網(wǎng)絡(luò)框架或服務(wù)器的相互操作
  1. 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ǔ)保證。
Tornado核心模塊

設(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事件
設(shè)計(jì)模型

使用Tornado可以方便地架構(gòu)出各種類型的web服務(wù)器挑豌,以HTTP服務(wù)器為例來(lái)分析下web服務(wù)器的工作方式。

服務(wù)器工作方式

一個(gè)請(qǐng)求處理的處理過(guò)程

  1. 服務(wù)器綁定bind到特定端口port墩崩,然后開始監(jiān)聽listen客戶端的請(qǐng)求氓英。
  2. 當(dāng)客戶端連接connect到來(lái)時(shí),會(huì)將請(qǐng)求發(fā)送給服務(wù)器鹦筹。
  3. 服務(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模式”

Tornado網(wǎng)絡(luò)模型

安裝配置

參考資料

版本問(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è)必備步驟:

  1. 創(chuàng)建socket
  2. 綁定指定地址的端口
  3. 執(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)境下。

ioloop

當(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ù)...

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末裕便,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子狭握,更是在濱河造成了極大的恐慌闪金,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異哎垦,居然都是意外死亡囱嫩,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門漏设,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)墨闲,“玉大人,你說(shuō)我怎么就攤上這事郑口≡П蹋” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵犬性,是天一觀的道長(zhǎng)瞻离。 經(jīng)常有香客問(wèn)我,道長(zhǎng)乒裆,這世上最難降的妖魔是什么套利? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮鹤耍,結(jié)果婚禮上肉迫,老公的妹妹穿的比我還像新娘。我一直安慰自己稿黄,他們只是感情好喊衫,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著杆怕,像睡著了一般族购。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上陵珍,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天联四,我揣著相機(jī)與錄音,去河邊找鬼撑教。 笑死,一個(gè)胖子當(dāng)著我的面吹牛醉拓,可吹牛的內(nèi)容都是我干的伟姐。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼亿卤,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼愤兵!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起排吴,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤秆乳,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體屹堰,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡肛冶,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了扯键。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片睦袖。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖荣刑,靈堂內(nèi)的尸體忽然破棺而出馅笙,到底是詐尸還是另有隱情,我是刑警寧澤厉亏,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布董习,位于F島的核電站,受9級(jí)特大地震影響爱只,放射性物質(zhì)發(fā)生泄漏皿淋。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一虱颗、第九天 我趴在偏房一處隱蔽的房頂上張望沥匈。 院中可真熱鬧,春花似錦忘渔、人聲如沸高帖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)散址。三九已至,卻和暖如春宣赔,著一層夾襖步出監(jiān)牢的瞬間预麸,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工儒将, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留吏祸,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓钩蚊,卻偏偏與公主長(zhǎng)得像贡翘,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子砰逻,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容