HTTP協(xié)議是建立在TCP基礎(chǔ)之上的應(yīng)用層協(xié)議饮焦,Tornado中TCP層由TCPServer
和IOStream
負(fù)責(zé),對HTTP報文的讀取與解析則是由http1connection
負(fù)責(zé)的。當(dāng)HTTP報文被解析完畢后再交由某個delegate
代理類實例負(fù)責(zé)進(jìn)行后續(xù)處理。
HTTP服務(wù)器工作流程
Web服務(wù)器工作的三部曲是什么呢蚜锨?
- 創(chuàng)建:服務(wù)器創(chuàng)建Socket并在指定地址的端口上進(jìn)行監(jiān)聽,等待客戶端請求的到來慢蜓。
- 監(jiān)聽:監(jiān)聽的Socket接收客戶端的請求亚再,當(dāng)?shù)玫娇蛻舳说腟ocket時,通過客戶端Socket與客戶端保持通信晨抡。
- 處理:處理客戶端的請求氛悬,首先客戶端的Socket中讀取HTTP請求的協(xié)議頭,如果是POST則可能需要讀取客戶端上傳的數(shù)據(jù)耘柱,讓然后處理請求如捅。最后準(zhǔn)備好客戶端所需的數(shù)據(jù)后通過客戶端Socket回寫給客戶端。
例如:使用Python編寫HTTP服務(wù)器
$ vim server.py
#!/usr/bin/python3
# -*- coding:utf-8 -*-
import socket
def handle_request(client):
buf = client.recv(1024)
print(buf)
client.send(bytes("HTTP/1.1 200 OK\r\n\r\n", "utf-8"))
client.send(bytes("hello world", "utf-8"))
def main():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(("127.0.0.1", 8000))
sock.listen(3)
while True:
connection, address = sock.accept()
handle_request(connection)
connection.close()
if __name__ == "__main__":
main()
$ python3 server.py
調(diào)試運行
$ curl http://127.0.0.1:8000
hello world
Tornado的HTTP服務(wù)器工作流程
Tornado Web程序編程思路
- 創(chuàng)建Web應(yīng)用程序?qū)嵗龑ο?/li>
- 定義實現(xiàn)路由映射表中的請求多處理器類
- 創(chuàng)建服務(wù)器實例并綁定端口
- 啟動當(dāng)前線程的事件循環(huán)
根據(jù)上述分析會將Web框架劃分為兩大部分
- 待請求階段
創(chuàng)建服務(wù)器Socket并監(jiān)聽端口 - 處理請求階段
當(dāng)有客戶端連接時接收請求并根據(jù)請求的不同做出相應(yīng)的響應(yīng)
當(dāng)Tornado程序啟動以及接收到客戶端請求的過程可分為兩部分
- 啟動程序階段/待請求階段
- 第一步:獲取配置文件然后生成URL映射调煎,即一個URL對應(yīng)一個
RequestHandler
請求處理器镜遣,從而使請求處理器來處理制定URL發(fā)送的請求。 - 第二步:創(chuàng)建服務(wù)器Socket對象并添加到Epoll中
- 第三步:創(chuàng)建無限循環(huán)去監(jiān)聽Epoll
- 接收并處理請求階段
- 第一步:接收客戶端Socket發(fā)送的請求
socket.accept
- 第二步:從請求中獲取請求頭信息士袄,在根據(jù)請求頭中的請求URL去匹配對應(yīng)請求處理器
RequestHandler
悲关。 - 第三步:成功匹配請求處理器
- 第四步:將處理后的請求發(fā)送給客戶端
- 第五步:關(guān)閉客戶端Socket
例如:使用Tornado實現(xiàn)單進(jìn)程的HTTP服務(wù)器
$ vim server.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from tornado.options import define, options
from tornado.web import Application, RequestHandler
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop
# 自定義配置項
define("port", type=int, default=8000)
define("debug", type=bool, default=True)
# 請求處理器
class IndexHandler(RequestHandler):
def get(self):
print("index handler get method")
self.write("hello world")
# 創(chuàng)建應(yīng)用
# 定義路由表
urls = [
(r"/index", IndexHandler)
]
# 定義應(yīng)用配置
configs = dict(debug = options.debug)
# 指定路由和配置創(chuàng)建應(yīng)用程序?qū)ο?app = Application(urls, configs)
# 程序主入口
if __name__ == "__main__":
# 解析命令行參數(shù)
options.parse_command_line()
# 為應(yīng)用創(chuàng)建HTTP服務(wù)器
server = HTTPServer(app)
print("http server start")
# 為HTTP服務(wù)器設(shè)置監(jiān)聽端口
server.listen(options.port)
print("http server listen port %s" % options.port)
# 開啟事件循環(huán)接收客戶端連接
IOLoop.current().start()
$ python server.py
查看進(jìn)程
$ ps -ef | grep server.py
jc 5047 3946 1 00:50 pts/0 00:00:00 python server.py
jc 5051 4169 0 00:50 pts/1 00:00:00 grep --color=auto server.py
訪問測試
$ curl http://127.0.0.1:8000/index
hello world
注意
-
listen()
方法只能在單進(jìn)程模式中使用
例如:使用Tornado實現(xiàn)多進(jìn)程的HTTP服務(wù)器
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from tornado.options import define, options
from tornado.web import Application, RequestHandler
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop
# 自定義配置項
# 定義端口號
define("port", type=int, default=8000)
# 定義調(diào)試模式
define("debug", type=bool, default=True)
# 定義子進(jìn)程個數(shù)
define("process", type=int, default=2)
# 請求處理器
class IndexHandler(RequestHandler):
def get(self):
print("index handler get method")
self.write("hello world")
# 創(chuàng)建應(yīng)用
# 定義路由表
urls = [
(r"/index", IndexHandler)
]
# 定義應(yīng)用配置
configs = dict(debug = options.debug)
# 指定路由和配置創(chuàng)建應(yīng)用程序?qū)ο?app = Application(urls, configs)
# 程序主入口
if __name__ == "__main__":
# 解析命令行參數(shù)
options.parse_command_line()
# 為應(yīng)用創(chuàng)建HTTP服務(wù)器
server = HTTPServer(app)
print("http server start")
# 為HTTP服務(wù)器綁定監(jiān)聽端口
server.bind(options.port)
# 為HTTP服務(wù)器開啟監(jiān)聽,并設(shè)置開啟進(jìn)程的數(shù)量num_process。
# 多進(jìn)程數(shù)量默認(rèn)為1
# 多進(jìn)程數(shù)量若小于等于0或為None則啟動會根據(jù)及其硬件的CPU核心數(shù)量創(chuàng)建等同數(shù)量的子進(jìn)程
# 多進(jìn)程數(shù)量大于0則表示實際創(chuàng)建子進(jìn)程的數(shù)量
server.start(options.process)
print("http server listen port %s" % options.port)
# 開啟事件循環(huán)接收客戶端連接
IOLoop.current().start()
開啟服務(wù)并查看進(jìn)程數(shù)量
$ python server.py
$ ps -ef | grep server.py
jc 5079 3946 1 00:59 pts/0 00:00:00 python server.py
jc 5080 5079 0 00:59 pts/0 00:00:00 python server.py
jc 5081 5079 0 00:59 pts/0 00:00:00 python server.py
jc 5084 4169 0 00:59 pts/1 00:00:00 grep --color=auto server.py
注意
- 每個子進(jìn)程都會從父進(jìn)程中復(fù)制一份
IOLoop
事件循環(huán)實例娄柳,若在創(chuàng)建子進(jìn)程前修改了IOLoop
實例則會影響到每個子進(jìn)程寓辱。 - 所有進(jìn)程是由一條命令一次性開啟的,所以是無法做到在不停服的情況下更新代碼赤拒。
- 所有進(jìn)程共享同一個端口
當(dāng)使用Tornado作為服務(wù)端時的工作流程是什么樣的呢秫筏?
- Tornado在連接建立完畢后會將連接封裝成為一個
IOStream
對象,這個對象可以異步的從連接中讀寫數(shù)據(jù)需了。 - Tornado中實現(xiàn)了
HTTP1ServerConnection
和HTTP1Connection
兩個類,它們依賴于底層的IOStream
并從Socket中讀寫般甲,并共同何做完成了HTTP 1.X協(xié)議肋乍。 -
HTTP1Connection
實際上主要用來處理HTTP事務(wù),也就是由一條請求以及對應(yīng)該請求的響應(yīng)所組成的事務(wù)敷存。HTTP1Connection
實現(xiàn)了HTTP事務(wù)前半部分也就是對報文起始行墓造、首行堪伍、主體進(jìn)行讀取并解析,而HTTP事務(wù)的后半部分則需要配合HTTPMessageDeletegate
進(jìn)行處理觅闽。 -
HTTPMessageDelegate
對經(jīng)過HTTP1Connection
解析后的報文會進(jìn)行分配帝雇,然后由其他類如RequestHandler
來執(zhí)行具體的業(yè)務(wù)邏輯。其他類會生成響應(yīng)并將響應(yīng)發(fā)送到IOStream
中蛉拙,此時才表示這條HTTP事務(wù)已經(jīng)完成尸闸。最后開發(fā)人員可以根據(jù)實際情況判斷是否關(guān)閉連接。 -
HTTP1ServerConnection
則會不斷地生成HTTP1Connection
實例孕锄,也就是不斷的處理HTTP事務(wù)吮廉,直至連接關(guān)閉為止。
Tornado服務(wù)器工作流程
首先按照socket->bind->listen
的順序創(chuàng)建listen socket
監(jiān)聽客戶端畸肆,并將每個listen socket
的文件描述符fd
注冊到IOLoop
的單例實例中宦芦,當(dāng)listen socket
可讀寫時回調(diào)_handle_events
處理客戶端請求,在與客戶端通信的過程中使用IOStream
封裝了讀和寫緩沖區(qū)轴脐,實現(xiàn)與客戶端的異步讀寫调卑。
Tornado服務(wù)器核心模塊
- IOLoop
Tornado為了實現(xiàn)高并發(fā)和高性能使用了一個IOLoop來處理Socket的讀寫事件,IOLoop是基于Linux的Epoll大咱,可以高效地響應(yīng)網(wǎng)絡(luò)事件恬涧,這也是Tornado高效的保證。
- IOStream
為了在處理請求時實現(xiàn)對Socket的異步讀寫徽级,Tornado實現(xiàn)了IOStream類用來處理Socket的異步讀寫操作气破。
- HTTPConnect
HTTPConnect類是用來處理HTTP請求,包括HTTP請求頭餐抢、讀取POST數(shù)據(jù)现使,調(diào)用用戶自定義的處理方法,以及把響應(yīng)數(shù)據(jù)寫給客戶端的Socket旷痕。
tornado.httpserver
HTTPServer
繼承于TCPServer
碳锈,其__init__
初始化函數(shù)中記錄了連接到來時的回調(diào)函數(shù)即application
對象的__call__
方法被觸發(fā),然后是父類的初始化欺抗。父類TCPServer
初始化主要是監(jiān)聽在特定地址的端口售碳,當(dāng)客戶端發(fā)起連接時服務(wù)器接收并調(diào)用handle_stream
方法。由于HTTPServer
覆蓋了父類TCPServer
的handle_stream
方法绞呈,并在該方法中生成HTTPConnection
對象贸人。HTTPConnection
對象被構(gòu)造時就立即開始了HTTP協(xié)議的處理。由于handle_stream
被調(diào)用時是新連接的到來佃声,此時緩沖區(qū)中都會有是有數(shù)據(jù)可讀的艺智。
class HTTPServer(
TCPServer,
Configurable,
httputil.HTTPServerConnectionDeletegate
):
-
HTTPServer
是TCPServer
的子類,關(guān)于Socket連接部分由TCPServer
類實現(xiàn)圾亏。 -
HTTPServer
只用于實現(xiàn):處理連接connection
和生成請求request
為了向后兼容十拣,HTTPServer
是HTTPServerConnectionDelegate
的子類封拧,有一個將HTTPServerRequest
作為參數(shù)的回調(diào)函數(shù)。這個委托delegate
一般是tornado.web.Application
夭问。
如果客戶端的請求頭中指定了Connection:keep-alive
的話泽西,HTTPServer
支持活動的連接。而且HTTPServer
是默認(rèn)支持活動連接的缰趋。
如果xheaders
選擇設(shè)置成了True
捧杉,HTTPServer同樣支持x-Real-Ip/X-Forwarded-For
以及X-Scheme/X-Forwarded-proto
請求頭,這樣對所有的請求都會覆蓋遠(yuǎn)程IP和 URL 協(xié)議埠胖。當(dāng)在運行反向代理或負(fù)載均衡時糠溜,這些頭文件會很有用。
- 處理連接
handle_stream
HTTPServer
默認(rèn)需要重載父類TCPServer
的handle_stream
方法用來處理連接和生成請求
def handle_stream(self, stream, address):
# 生成請求上下文
context = _HTTPRequestContext(stream, addres, self.protocol)
# 生成請求
conn = HTTPServerConnection(stream, self.conn_params, context)
# 加入到連接池直撤,請求結(jié)束時需移除
self._connections.add(conn)
# 該連接開啟服務(wù)
conn.start_service(self)
- 生成請求
start_request
# 開始處理請求
def start_request(self, server_conn, request_conn):
# 交由_ServerRequestAdapter生成Request請求
return _ServerRequestAdapter(self, server_conn, request_conn)
HTTPServer的構(gòu)造函數(shù)__init__
中最重要的是request_callback
非竿,也就是說,當(dāng)一個請求過來時應(yīng)該如何處理谋竖。
Tornado的httpserver
模塊中具有HTTPServer
红柱、HTTPConnection
、HTTPRequest
三個類蓖乘。
#!/usr/bin/python3
# -*- coding:utf-8 -*-
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop
def handle_request(request):
message = "method:{0}\nuri:{1}\nversion={2}".format(request.method, request.uri, request.version)
for k,v in request.headers.items():
message += k + ":" + v + "\n"
request.connection.write(message)
request.connection.finish()
def main():
server = HTTPServer(handle_request, False)
server.listen(8000)
IOLoop.instance().start()
if __name__=="__main__":
main()
-
HTTPServer
是一個簡單的HTTP服務(wù)器锤悄,主要作用是創(chuàng)建監(jiān)聽Socket,設(shè)置監(jiān)聽Socket的讀事件處理器嘉抒,最后調(diào)用HTTPConnection
類處理整個連接零聚。 -
HTTPConnection
用于讀取請求并解析,調(diào)用HTTPServer
設(shè)置的回調(diào)函數(shù)(一般是Application)處理請求些侍,并提供發(fā)送響應(yīng)的接口隶症。
httpserver
模塊是Tornado的HTTP服務(wù)器實現(xiàn)
引入tornado.httpserver
模塊
from tornado.httpserver import HTTPServer
創(chuàng)建HTTP服務(wù)器實例
urls = [
(r"/", MainHandler)
]
app = tornado.web.Application(urls, debug=True)
httpServer = tornado.httpserver.HTTPServer(app)
監(jiān)聽端口,將服務(wù)器綁定到指定端口岗宣。
listen()
方法只能在單進(jìn)程模式中使用蚂会,若在多進(jìn)程中可使用bind()
方法和start()
方法相結(jié)合。
單進(jìn)程監(jiān)聽端口
httpServer.listen(8000)
多進(jìn)程綁定并監(jiān)聽端口
# 將服務(wù)器綁定到指定端口
httpServer.bind(port)
# 指定開啟進(jìn)程的數(shù)量耗式,num_processes表示進(jìn)程的數(shù)量胁住,默認(rèn)為1.
# 若num_processes小于0或為None則會自動根據(jù)機(jī)器硬件的CPU核心數(shù)量創(chuàng)建等數(shù)量子進(jìn)程,若num_processes大于0則會創(chuàng)建與num_processes相同數(shù)量的子進(jìn)程刊咳。
httpServer.start(num_processes = 1)
HTTPConnection
HTTPConnection
是HTTP協(xié)議在服務(wù)器的真正實現(xiàn)者彪见,對于請求的讀取和解析基本上是依賴HTTPHeaders
完成的,對于響應(yīng)方面的處理包括響應(yīng)頭娱挨、響應(yīng)體的中間處理等在是在RequestHandler
的flush
方法和finish
方法中完成的余指。
tornado.web
Tornado的Web框架tornado.web
是在web.py
文件中實現(xiàn)的,主要包含RequestHandler
類和Application
類让蕾。
-
RequestHandler
類是對HTTP請求處理的封裝 -
Application
類是一些列請求的集合浪规,構(gòu)成一個Web應(yīng)用程序。
A collection of request handleres that make up a web application
from tornado.web import RequestHandler, Application
RequestHandler
RequestHandler
提供了一個針對HTTP請求處理的基類封裝探孝。主要功能包括:
- 提供
GET
/POST
/HEAD
/DELETE
/PATCH
/PUT
/OPTIONS
等方法的功能接口笋婿,開發(fā)時RequestHandler
的子類重寫這些方法以支持不同需求的請求處理。 - 提供對HTTP請求的處理方法顿颅,包括對
headers
缸濒、頁面元素、cookie
的處理粱腻。 - 提供對請求響應(yīng)的方法庇配,包括
redirect
頁面沖重定向、write
將數(shù)據(jù)寫入IO緩沖區(qū)绍些、render
渲染模板等捞慌。 - 提供輔助功能,如結(jié)束請求和響應(yīng)柬批、刷新輸出緩沖區(qū)啸澡、對用戶授權(quán)等處理。
RequestHandler
封裝了對應(yīng)請求的所有信息和方法氮帐,利用HTTP向服務(wù)器傳參的方式分為
- 通過
GET
方法的查詢字符串querystring
嗅虏,形式為key1=value1&key2=value2&key3=value3...
。 - 通過
POST
方法的請求體body
中發(fā)送的數(shù)據(jù)上沐,比如表單數(shù)據(jù)皮服、json
、xml
... - 通過
GET
方法提取URI中的特定部分参咙,比如PATHINFO
模式中/blogs/2019/07/09
龄广,通過服務(wù)器路由中的正則表達(dá)式進(jìn)行提取。 - 通過HTTP報文的頭
header
中增加自定義字段昂勒,如X-XSRFToken=xxxx
蜀细。
使用Tornado獲取請求參數(shù)的方式
- 獲取
GET
請求中查詢字符串querystring
參數(shù)
-
get_query_argument
獲取查詢字符串querystring
中的指定參數(shù)
val = get_query_argument(name, default=_ARG_DEFAULT, strip=True)
get_query_argument
會從請求查詢字符串中獲取指定參數(shù)名name
的值,如果出現(xiàn)多個同名參數(shù)則返回最后一個值戈盈。
參數(shù)列表
參數(shù)1:name
表示查詢字符串中指定的參數(shù)名
參數(shù)2:default
表示當(dāng)未傳入name
參數(shù)時返回的默認(rèn)值奠衔,若default
默認(rèn)值未設(shè)置則會拋出tornado.web.MissingArgumentError
異常。
參數(shù)3:strip
表示是否過濾左右兩邊的空白字符塘娶,默認(rèn)過濾strip=True
归斤。
例如
$ vim server.php
#! /usr/bin/python3
# encoding=utf-8
from tornado.options import define, options
from tornado.web import Application, RequestHandler, url
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop
define("port", type=int, default=8000, help="run server on the given port")
class IndexHandler(RequestHandler):
def get(self):
self.write("hello world")
def post(self):
self.write("post")
class UserHandler(RequestHandler):
def get(self):
uid = self.get_query_argument("uid", strip=True)
print("uid = " + str(uid))
username = self.get_query_argument("username", strip=True)
print("username = %s" %username.encode("utf-8"))
index_url = self.reverse_url("index_url")
print(index_url)
self.write("success")
# self.write("uid=%s username=%s index_url=%s" %uid %username.encode("utf-8") %index_url)
urls = [
url(r"/", IndexHandler, name="index_url"),
(r"/user", UserHandler)
]
settings = dict(debug=True)
if __name__ == "__main__":
options.parse_command_line()
app = Application(urls, settings)
server = HTTPServer(app)
server.listen(options.port)
IOLoop.current().start()
運行服務(wù)器
$ python server.php
客戶端訪問,注意Linux下&
取地址符表示進(jìn)程后臺運行刁岸,所以在URL中必須進(jìn)行轉(zhuǎn)義才能獲取到參數(shù)脏里。
$ curl http://127.0.0.1:8000/user?uid=100\&username=中文
-
get_query_arguments
獲取查詢字符串querystring
中參數(shù)
list = get_query_arguments(name, strip=True)
get_query_arguments
用于從請求的查詢字符串中返回指定參數(shù)名name
的值,返回的是list
列表虹曙,若未找到name
參數(shù)則返回空列表[]
迫横。
- 獲取
POST
請求體body
參數(shù)
-
get_body_argument
獲取請求體參數(shù)
對于HTTP請求體body
中的數(shù)據(jù)番舆,必須要求格式為字符串且表單編碼格式與URL中的請求字符串格式一致,即key1=value1&key2=value2&key3=value3...
的形式矾踱。
HTTP報文頭Header
中的Content-Type
內(nèi)容類型必須為application/x-www-form-urlencoded
或multipart/form-data
恨狈。對于請求體數(shù)據(jù)為JSON
或XML
的則無法獲取。
val = get_body_argument(name, default=_ARG_DEFAULT, strip=True)
get_body_argument
會從請求體body
中獲取指定參數(shù)名name
的值呛讲,若出現(xiàn)多個同名參數(shù)則返回最后一個的值禾怠。
-
get_body_arguments
獲取請求體參數(shù)
list = get_body_arguments(name, strip=True)
get_body_arguments
表示從請求體中返回指定參數(shù)名name
的值,返回list
列表贝搁,若未找到name
參數(shù)則返回空列表[]
吗氏。
- 獲取請求參數(shù)
get_argument
val = get_argument(name, default=_ARG_DEFAULT, strip=True)
get_argument
表示從POST
請求體body
和GET
查詢字符串querystring
中返回指定參數(shù)名name
的值,如果出現(xiàn)多個同名參數(shù)則返回最后一個的值雷逆。
get_arguments
list = get_arguments(name, strip=True)
get_arguments
表示從POST
請求體body
或GET
查詢字符串querystring
中返回指定參數(shù)名name
的值弦讽,返回值類型為list
列表,若為找到name
參數(shù)則返回空列表[]
膀哲。
- 獲取請求信息
RequestHandler.request
對象存儲了請求的相關(guān)信息坦袍,具體屬性包括:
-
RequestHandler.request.method
表示HTTP的請求方式,如GET
或POST
等太。 -
RequestHandler.request.host
表示HTTP中被請求的主機(jī)名 -
RequestHandler.request.uri
表示HTTP請求的完整資源標(biāo)識捂齐,包括路徑和查詢字符串。 -
RequestHandler.request.path
表示HTTP請求中URL的路徑部分 -
RequestHandler.request.query
表示HTTP請求中URL的查詢字符串部分 -
RequestHandler.request.version
表示HTTP請求所使用的HTTP版本號 -
RequestHandler.request.headers
表示HTTP請求的協(xié)議頭缩抡,是類字典型的對象奠宜,支持關(guān)鍵字索引的方式獲取特定協(xié)議頭字段,如request.headers["Content-Type"]
瞻想。 -
RequestHandler.request.body
表示HTTP請求體數(shù)據(jù) -
RequestHandler.request.remote_ip
表示HTTP請求中客戶端的IP地址 -
RequesteHandler.request.files
表示HTTP請求中用戶上傳的文件压真,字典類型。
# form_filename1字典中的鍵名表示的是表單對應(yīng)項的名字
request.files["form_filename1"][0]["body"]
文件上傳
文件上傳時可使用tornado.httputil.HTTPFile
接收文件蘑险,HTTPFile
是接收到的文件對象滴肿,它包含三個屬性。
- 屬性1:
HTTPFile.filename
表示文件的實際名字 - 屬性2:
HTTPFile.body
表示文件的數(shù)據(jù)實體 - 屬性3:
HTTPFile.content_type
表示文件的類型
HTTPFile
中的三個對象屬性可以像字典一樣支持關(guān)鍵字索引佃迄。
輸出數(shù)據(jù)流
write(chunk)
write
將chunk
數(shù)據(jù)寫入到緩沖區(qū)泼差,如果多次使用write
方法則會不斷追加響應(yīng)內(nèi)容,最終會將所有寫入到緩沖區(qū)的內(nèi)容一起作為本次請求的響應(yīng)一起輸出呵俏。
如果傳入的chunk
是dict
字典類型堆缘,Tornado會自動將其識別為JSON,并設(shè)置Header
報頭的Content-Type
字段為application/json
普碎。
如果傳入的chunk
是list
列表類型吼肥,考慮到安全問題list
列表將不會被轉(zhuǎn)換為JSON格式。
finish(chunk)
finish
表示完成響應(yīng)并結(jié)束本次請求,通常情況下請求會在return
時自動調(diào)用finish
方法缀皱,只有在使用異步裝飾器@asynchronous
或其他將._auto_finish
設(shè)置為False
時才需要手動調(diào)用finish
方法斗这。
#! /usr/bin/python3
# encoding=utf-8
from tornado.options import define, options
from tornado.web import Application, RequestHandler, url
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop
define("port", type=int, default=8000, help="run server on the given port")
class IndexHandler(RequestHandler):
def get(self):
self.write("hello world")
def post(self):
self.write("post")
class UserHandler(RequestHandler):
def get(self):
uid = self.get_query_argument("uid", strip=True)
print("uid = " + str(uid))
username = self.get_query_argument("username", strip=True)
print("username = %s" %username)
index_url = self.reverse_url("index_url")
print(index_url)
jsonstr= {"uid":uid, "username":username,"url":index_url}
self.finish(jsonstr)
urls = [
url(r"/", IndexHandler, name="index_url"),
(r"/user", UserHandler)
]
settings = dict(debug=True)
if __name__ == "__main__":
options.parse_command_line()
app = Application(urls, settings)
server = HTTPServer(app)
server.listen(options.port)
IOLoop.current().start()
flush()
flush
方法會將緩沖區(qū)的數(shù)據(jù)寫入到socket
中,如果設(shè)置了回調(diào)函數(shù)callback
啤斗,會在完成數(shù)據(jù)寫入后回調(diào)涝影。需要在注意的是同一時間只能有一個等待的flush callback
,如果上一次的flush callback
還沒有執(zhí)行争占,又來了新的flush
則上一次的flush callback
會被忽略掉。
Application
A collection of request handle that mak up a web application. Instances of this class are callbable and can be passed directly to HTTPServer to server the application.
Application類初始化
Application
是Tornado Web框架的核心應(yīng)用類序目,是與服務(wù)器對接的接口臂痕。保存了路由信息,其初始化的第一個 參數(shù)是一個路由映射元組的列表猿涨,可使用listen
方法創(chuàng)建一個HTTP服務(wù)器實例并綁定指定的端口握童。
from tornado.web import Application
app = Application(urls, settings)
Application
類初始化的第一個參數(shù)是接收一個(regexp, requste_class)
形式的列表,參數(shù)指定了針對不同URL請求所采取的處理方法叛赚,包括對靜態(tài)文件請求的處理web.StaticFileHandler
澡绩。
Application
類中實現(xiàn)了__call__
調(diào)用函數(shù),這樣該類就成為了可調(diào)用的對象俺附,并由HTTPServer
來進(jìn)行調(diào)用肥卡。__call__
函數(shù)會遍歷Application
類中的handlers
列表,匹配到相應(yīng)的URL后通過handler._execute
進(jìn)行相應(yīng)處理事镣。如果沒有匹配到URL則會調(diào)用ErrorHandler
步鉴。
參數(shù)1:路由映射表urls
在構(gòu)建路由映射表時使用的是二元元組
urls = [(r"/", IndexHandler),]
對于路由映射表中的路由可傳入多個路由規(guī)則
from tornado.web import Application, RequestHandler, url
urls = [
(r"/", IndexHandler),
(r"/create", CreateHandler, {"pid": 100}),
url(r"/update", UpdateHandler, {"id":100}, name="update_url")
]
路由中的字典{k:v}
會傳入到對應(yīng)的RequestHandler
的initialize()
方法中
from tornado.web import RequestHandler, url
class TestHandler(RequestHandler):
def initialize(self, k):
self.k = k
def get(self):
self.write(self.k)
urls = [
(r"/", TestHandler, {k:v})
]
app = tornado.web.Application(urls)
路由中的name
字段不能再使用元組而應(yīng)使用tornado.web.url
來構(gòu)建,name
是給路由起一個別名璃哟,可通過調(diào)用RequestHandler.reverse_url(name)
來獲取名字對應(yīng)的URL氛琢。
$ vim server.py
#! /usr/bin/python
# encoding=utf-8
from tornado.options import define, options
from tornado.web import Application, RequestHandler, url
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop
define("port", type=int, default=8000, help="run server on the given port")
class IndexHandler(RequestHandler):
def initialize(self, id):
self.id = id
def get(self):
id = self.get_query_argument("id", strip=True)
# self.write(id)
index_url = self.reverse_url("index_url")
self.write("url=%s" %index_url)
def post(self):
self.write("post")
urls = [
url(r"/", IndexHandler, {"id":100}, name="index_url")
]
settings = dict(debug=True)
if __name__ == "__main__":
options.parse_command_line()
app = Application(urls, settings)
server = HTTPServer(app)
server.listen(options.port)
IOLoop.current().start()
$ python server.py
參數(shù)2:應(yīng)用配置參數(shù)settings
Application
類傳遞給構(gòu)造器的附加關(guān)鍵字參數(shù)保存在settings
字典中,settings
被用于自定義Tornado應(yīng)用的多個方面随闪。
settins = dict(debug=True)
debug
用于設(shè)置Tornado是否工作在調(diào)試模式阳似,默認(rèn)為False即工作在生產(chǎn)模式,當(dāng)設(shè)置debug=True
后Tornado會工作在開發(fā)調(diào)試模式铐伴,在此種調(diào)試模式下Tornado提供了幾種特性:
- 自動重啟
Tornado應(yīng)用會監(jiān)控源代碼文件撮奏,當(dāng)有改動保存后會自動重啟程序,減少手工重啟程序的次數(shù)当宴。需要注意的時挽荡,一旦保存的更改有錯誤,自動重啟會導(dǎo)致程序報錯而退出即供,從而需要保存修正錯誤后手動啟動程序定拟。此特性可以通過autoreload=True
設(shè)置。
settings = dict(autoreload=True)
- 取消緩存編譯的模板
settings = dict(compiled_template_cache=False)
- 取消緩存靜態(tài)文件
hash
值
settings = dict(static_hash_cache=False)
- 提供追蹤信息
當(dāng)RequestHandler
或其子類拋出一個異常而未捕獲后,會生成一個包含追蹤信息的頁面青自。
settings = dict(serve_traceback=True)
設(shè)置應(yīng)用配置
import tornado.web
tornado.web.Application(urls, settings)
from tornado.web import Application, RequestHandler, url
urls = [
url(r"/", IndexHandler)
]
settings = dict(debug=True)
app = Application(urls, settings)
tornado.ioloop
Tonado的核心IO循環(huán)模塊是tornado.io
株依,它封裝了Linux的Epoll和BSD的kqueue,是Tornado高性能的基礎(chǔ)延窜。
-
tornado.ioloop
是全局Tornado的IO事件循環(huán)恋腕,是服務(wù)器的引擎核心。 -
tornado.ioloop
是核心IO循環(huán)模塊逆瑞,封裝了Linux的epoll和BSD的kqueue荠藤,是Tornado高性能處理的核心。 -
tornado.ioloop.IOLoop.current()
返回當(dāng)前線程的IOLoop實例對象 -
tornado.ioloop.IOLoop.current().start()
用于啟動IOLoop實例對象的IO循環(huán)并開啟監(jiān)聽
# 加載Tornado核心IO事件循環(huán)模塊
import tornado.ioloop
# 默認(rèn)Tornado的ioloop實例
tornado.ioloop.IOLoop.current()
tornado.options
from tornado.options import define, options
options
表示全局的options
對象获高,可用于全局參數(shù)的定義哈肖、存儲、轉(zhuǎn)換念秧,所有定義的選項變量都會作為options
對象的屬性淤井。
tornado.options.optins
define
方法用來定義options
選項變量,定義的變量可以在全局tornado.options.options
中獲取使用摊趾。
tornado.options.define(name, default, type, multiple, help)
-
name
表示選項的變量名稱币狠,必須保證全局唯一,否則會報Option xxx already defined in...
錯誤砾层。 -
default
表示選項變量的默認(rèn)值漩绵,若不傳入則默認(rèn)為None
。 -
type
表示選項變量的類型肛炮,從命令行或配置文件導(dǎo)入?yún)?shù)時Tornado會根據(jù)此類型轉(zhuǎn)換輸入的值渐行,若轉(zhuǎn)換 失敗則 報錯,轉(zhuǎn)換的類型支持str
铸董、float
祟印、int
、datetime
粟害、timedate
蕴忆。若未設(shè)置則會根據(jù)default
的值自動進(jìn)行推斷,若default
默認(rèn)值也沒有設(shè)置則不會再進(jìn)行轉(zhuǎn)換悲幅,可以通過利用設(shè)置type
類型字段來過濾不正確的輸入套鹅。 -
multiple
選項變量的值是否可為多個,布爾類型汰具,默認(rèn)為False
卓鹿,如果multiple
為True
則設(shè)置選項變量時值與值之間使用英文逗號分隔,此時選項變量是一個list
列表留荔。若默認(rèn)值和輸入均未設(shè)置則為空列表[]
吟孙。 -
help
選項變量的幫助提示信息,在命令行啟動Tornado時會加入命令行參數(shù)--help
,可以查看所有選項變量的信息杰妓。
define
用于定義端口用于指定HTTP服務(wù)監(jiān)聽的端口藻治,如果命令行中帶有port
同名參數(shù)則會稱為全局tornado.options
的屬性,若沒有則使用define
定義巷挥。
tornado.options.define('port', default=8000, type=int, help="this is the port >for application")
parse_command_line
轉(zhuǎn)換命令行參數(shù)桩卵,并將轉(zhuǎn)換后的值對應(yīng)的設(shè)置到全局options
對象相關(guān)屬性上。
tornado.options.parse_command_line()
命令行追加參數(shù)的方式是使用--myoption=myvalue
$ python server.py --port=8000
parse_config_file
根據(jù)配置文件配置文件獲取參數(shù)
tornado.options.parse_config_file(filepath)
從配置文件導(dǎo)入option
時配置文件中的格式為
tornado.options.parse_config_file("./config")
使用parse_config_file()
函數(shù)時配置文件的編寫格式需按照Python的語法要求倍宾,其優(yōu)勢是可以直接將配置文件的參數(shù)文件轉(zhuǎn)換設(shè)置到全局對象tornado.options.options
上雏节。
$ vim config.py
port = 8000
debug = True
ip = ["192.168.56.100", "192.168.56.101", "192.168.56.102"]