服務(wù)器
接收客戶端的請(qǐng)求症革,返回響應(yīng)給客戶端筐咧。客戶端與服務(wù)器之間使用 HTTP 協(xié)議通信。
Apache量蕊、Nginx铺罢、Lighttpd 就是較為常用的企業(yè)級(jí)服務(wù)器。Gunicorn 是由 Python 編寫的小只服務(wù)器残炮。Werkzeug 支持單進(jìn)程多線程服務(wù)韭赘,Gunicorn 實(shí)現(xiàn)多進(jìn)程服務(wù)。
協(xié)議
在計(jì)算機(jī)科學(xué)中势就,指的是雙方通信所共同遵循的數(shù)據(jù)格式和通信步驟泉瞻。
WSGI
Web Server Gateway Interface 的簡(jiǎn)稱,它是一種規(guī)定了服務(wù)器與 Web 應(yīng)用之間如何通信的協(xié)議苞冯。這是 Python 專用的袖牙,其它編程語(yǔ)言有自己的協(xié)議。
WSGI 協(xié)議是基于現(xiàn)存的 CGI 標(biāo)準(zhǔn)設(shè)計(jì)的舅锄。該協(xié)議要求開(kāi)發(fā)者提供一個(gè) application 函數(shù)鞭达,類似下面這樣:
def application(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
return [b'<h1>Hello, web!</h1>']
服務(wù)器接收請(qǐng)求后,處理一下請(qǐng)求信息皇忿,將 Request Headers 放到 environ 變量中畴蹭,這是個(gè)字典對(duì)象。然后調(diào)用上面的 application 函數(shù)禁添,這個(gè)函數(shù)由誰(shuí)提供呢撮胧?當(dāng)然是由應(yīng)用程序提供。在 application 內(nèi)部會(huì)調(diào)用 start_response 函數(shù)老翘,這是由服務(wù)器內(nèi)部提供的函數(shù)芹啥,它用于生成響應(yīng)對(duì)象。最后 application 函數(shù)返回列表铺峭,列表中是二進(jìn)制字符串墓怀。最后服務(wù)器將完整的響應(yīng)對(duì)象返回給服務(wù)器。
在這個(gè)過(guò)程中卫键,接收請(qǐng)求信息傀履、發(fā)送響應(yīng)信息由服務(wù)器來(lái)做。應(yīng)用程序要做的是根據(jù)請(qǐng)求信息提供對(duì)應(yīng)的數(shù)據(jù)給服務(wù)器莉炉,包括響應(yīng)狀態(tài)碼钓账、HTML 數(shù)據(jù)、響應(yīng)頭部之類的絮宁。
你可以選擇任意服務(wù)器搭配任意 Web 應(yīng)用程序梆暮,只要它們都符合 WSGI 協(xié)議即可互相通信。
適用于 Web 服務(wù)器與應(yīng)用程序之間通信協(xié)議绍昂,除了 WSGI 外還有 uwsgi 協(xié)議以及 uWSGI 協(xié)議啦粹。最后一個(gè)是前兩個(gè)的集合協(xié)議偿荷。
WSGI 服務(wù)器
符合 WSGI 協(xié)議的服務(wù)器。如果不符合的話怎么辦唠椭?uwsgi 或者 uWSGI 也是可以的跳纳。
Flask 、Django 贪嫂、Tornado
這些是 Python Web 開(kāi)發(fā)框架寺庄,它們十分有助于編寫優(yōu)質(zhì)的 Web 應(yīng)用程序。Python 的語(yǔ)言特性使得自身編寫 Web 框架極其容易撩荣,以至于現(xiàn)在有上百種 Python 編寫的 Web 開(kāi)發(fā)框架铣揉。
Werkzeug
這是一個(gè) Flask 框架所依賴的一個(gè)工具包,它作為服務(wù)器與 Flask 程序之間的橋梁餐曹,實(shí)現(xiàn)了 WSGI 協(xié)議。實(shí)際上 Flask 中的請(qǐng)求和響應(yīng)對(duì)象都是由它來(lái)創(chuàng)建的敌厘。
其實(shí) Django 等其它框架也有類似的工具包台猴,沒(méi)有的話就不能根據(jù) WSGI 協(xié)議與服務(wù)器通信了,這很顯然俱两。
以下內(nèi)容來(lái)自網(wǎng)絡(luò)
Nginx:Hey饱狂,WSGI。我剛收到了一個(gè)請(qǐng)求宪彩,需要你作些準(zhǔn)備休讳,然后由 Flask 來(lái)處理。
WSGI:OK尿孔,Nginx俊柔。我會(huì)設(shè)置好環(huán)境變量,然后將這個(gè)請(qǐng)求傳遞給 Flask 處理活合。
Flask:Thanks WSGI雏婶!給我一些時(shí)間,我將會(huì)把請(qǐng)求的響應(yīng)返回給你白指。
Flask:Okay留晚,我完成了,WSGI告嘲。這里是請(qǐng)求的響應(yīng)結(jié)果错维,請(qǐng)求把結(jié)果傳遞給 Nginx。
WSGI:Good job橄唬,F(xiàn)lask赋焕!Nginx,這里是響應(yīng)結(jié)果轧坎,已經(jīng)按照要求給你傳遞回來(lái)了宏邮。
Nginx:Cool,我收到了,我把響應(yīng)結(jié)果返回給客戶端蜜氨。大家合作愉快~
收到請(qǐng)求后
假設(shè)應(yīng)用 app = Flask(__name__)
Werkzeug 會(huì)調(diào)用 app 本身械筛,也就是調(diào)用 app 的 __call__
方法:
# 參數(shù) environ 是字典對(duì)象,包含請(qǐng)求信息中的全部數(shù)據(jù)
# 例如 USER_AGENT 飒炎、IP 地址埋哟、端口號(hào)、Cookie 等等
def __call__(self, environ, start_response):
return self.wsgi_app(environ, start_response)
該方法返回 app 的 wsgi_app
方法的調(diào)用:
def wsgi_app(self, environ, start_response):
ctx = self.request_context(environ)
error = None
try:
try:
ctx.push()
response = self.full_dispatch_request()
except Exception as e:
error = e
response = self.handle_exception(e)
except: # noqa: B001
error = sys.exc_info()[1]
raise
return response(environ, start_response)
finally:
if self.should_ignore_error(error):
error = None
ctx.auto_pop(error)