到目前為止,為了簡(jiǎn)單起見(jiàn),在我們的例子中都是使用單一的Tornado進(jìn)程運(yùn)行的。這使得測(cè)試應(yīng)用和快速變更非常簡(jiǎn)單,但是這不是一個(gè)合適的部署策略。部署一個(gè)應(yīng)用到生產(chǎn)環(huán)境面臨著新的挑戰(zhàn),既包括最優(yōu)化性能,也包括管理獨(dú)立進(jìn)程。本章將介紹強(qiáng)化你的Tornado應(yīng)用、增加請(qǐng)求吞吐量的策略,以及使得部署Tornado服務(wù)器更容易的工具。
在大多數(shù)情況下,組合一個(gè)網(wǎng)頁(yè)不是一個(gè)特別的計(jì)算密集型處理。服務(wù)器需要解析請(qǐng)求,取得適當(dāng)?shù)臄?shù)據(jù),以及將多個(gè)組件組裝起來(lái)進(jìn)行響應(yīng)。如果你的應(yīng)用使用阻塞的調(diào)用查詢數(shù)據(jù)庫(kù)或訪問(wèn)文件系統(tǒng),那么服務(wù)器將不會(huì)在等待調(diào)用完成時(shí)響應(yīng)傳入的請(qǐng)求。在這些情況下,服務(wù)器硬件有剩余的CPU時(shí)間來(lái)等待I/O操作完成。
鑒于響應(yīng)一個(gè)HTTP請(qǐng)求的時(shí)間大部分都花費(fèi)在CPU空閑狀態(tài)下,我們希望利用這個(gè)停工時(shí)間,最大化給定時(shí)間內(nèi)我們可以處理的請(qǐng)求數(shù)量。也就是說(shuō),我們希望服務(wù)器能夠在處理已打開(kāi)的請(qǐng)求等待數(shù)據(jù)的過(guò)程中接收盡可能多的新請(qǐng)求。
正如我們?cè)诘谖逭掠懻摰漠惒紿TTP請(qǐng)求中所看到的,Tornado的非阻塞架構(gòu)在解決這類(lèi)問(wèn)題上大有幫助?;叵胍幌拢惒秸?qǐng)求允許Tornado進(jìn)程在等待出站請(qǐng)求返回時(shí)執(zhí)行傳入的請(qǐng)求。然而,我們碰到的問(wèn)題是當(dāng)同步函數(shù)調(diào)用塊時(shí)。設(shè)想在一個(gè)Tornado執(zhí)行的數(shù)據(jù)庫(kù)查詢或磁盤(pán)訪問(wèn)塊中,進(jìn)程不允許回應(yīng)新的請(qǐng)求。這個(gè)問(wèn)題最簡(jiǎn)單的解決方法是運(yùn)行多個(gè)解釋器的實(shí)例。通常情況下,你會(huì)使用一個(gè)反向代理,比如Nginx,來(lái)非配多個(gè)Tornado實(shí)例的加載。
一個(gè)代理服務(wù)器是一臺(tái)中轉(zhuǎn)客戶端資源請(qǐng)求到適當(dāng)?shù)姆?wù)器的機(jī)器。一些網(wǎng)絡(luò)安裝使用代理服務(wù)器過(guò)濾或緩存本地網(wǎng)絡(luò)機(jī)器到Internet的HTTP請(qǐng)求。因?yàn)槲覀儗⑦\(yùn)行一些在不同TCP端口上的Tornado實(shí)例,因此我們將使用反向代理服務(wù)器:客戶端通過(guò)Internet連接一個(gè)反向代理服務(wù)器,然后反向代理服務(wù)器發(fā)送請(qǐng)求到代理后端的Tornado服務(wù)器池中的任何一個(gè)主機(jī)。代理服務(wù)器被設(shè)置為對(duì)客戶端透明的,但它會(huì)向上游的Tornado節(jié)點(diǎn)傳遞一些有用信息,比如原始客戶端IP地址和TCP格式。
我們的服務(wù)器配置如圖8-1所示。反向代理接收所有傳入的HTTP請(qǐng)求,然后把它們分配給獨(dú)立的Tornado實(shí)例。
圖8-1 反向代理服務(wù)器后端的Tornado實(shí)例
代碼清單8-1中的列表是一個(gè)Nginx配置的示例。Nginx啟動(dòng)后監(jiān)聽(tīng)來(lái)自80端口的連接,然后分配這些請(qǐng)求到配置文件中列出的上游主機(jī)。在這種情況下,我們假定上游主機(jī)監(jiān)聽(tīng)來(lái)自他們自己的環(huán)回接口上的端口的連接。
代碼清單8-1 一個(gè)簡(jiǎn)單的Nginx代理配置
user nginx;
worker_processes 5;
error_log /var/log/nginx/error.log;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
use epoll;
}
proxy_next_upstream error;
upstream tornadoes {
server 127.0.0.1:8000;
server 127.0.0.1:8001;
server 127.0.0.1:8002;
server 127.0.0.1:8003;
}
server {
listen 80;
server_name www.example.org *.example.org;
location /static/ {
root /var/www/static;
if ($query_string) {
expires max;
}
}
location / {
proxy_pass_header Server;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_pass http://tornadoes;
}
}
這個(gè)配置示例假定你的系統(tǒng)使用了epoll。在不同的UNIX發(fā)行版本中經(jīng)常會(huì)有輕微的不同。一些系統(tǒng)可能使用了poll、/dev/poll或kqueue代替。
按順序來(lái)看匹配location /static/或location /的請(qǐng)求可能會(huì)很有幫助。Nginx把位于location指令中的字符串看作是一個(gè)以行起始錨點(diǎn)開(kāi)始、任何字母重復(fù)結(jié)束的正則表達(dá)式。所以/被看作是表達(dá)式^/.*。當(dāng)Nginx匹配這些字符串時(shí),像/static這樣更加特殊的字符串在像/這樣的更加的通用的字符串之前被檢查。Nginx文檔中詳細(xì)解釋了匹配的順序。
除了一些標(biāo)準(zhǔn)樣板外,這個(gè)配置文件最重要的部分是upstream指令和服務(wù)器配置中的proxy指令。Nginx服務(wù)器在80端口監(jiān)聽(tīng)連接,然后分配這種請(qǐng)求給upstream服務(wù)器組中列出的Tornado實(shí)例。proxy_pass指令指定接收轉(zhuǎn)發(fā)請(qǐng)求的服務(wù)器URI。你可以在proxy_pass URI中的主機(jī)部分引用upstream服務(wù)器組的名字。
Nginx默認(rèn)以循環(huán)的方式分配請(qǐng)求。此外,你也可以選擇基于客戶端的IP地址分配請(qǐng)求,這種情況下(除非連接中斷)可以確保來(lái)自同一IP地址的請(qǐng)求總是被分配到同一個(gè)上游節(jié)點(diǎn)。你可以在HTTPUpstreamModule文檔中了解更多關(guān)于這個(gè)選項(xiàng)的知識(shí)。
還需要注意的是location /static/指令,它告訴Nginx直接提供靜態(tài)目錄的文件,而不再代理請(qǐng)求到Tornado。Nginx可以比Tornado更高效地提供靜態(tài)文件,所以減少Tornado進(jìn)程中不必要的加載是非常有意義的。
應(yīng)用的開(kāi)發(fā)者在瀏覽器和客戶端之間傳輸個(gè)人信息時(shí)需要特別注意保護(hù)信息不要落入壞人之手。在不安全的WiFi接入中,用戶很容易受到cookie劫持攻擊,從而威脅他們?cè)诹餍械纳缃痪W(wǎng)站上的賬戶。對(duì)此,大部分主要的社交網(wǎng)絡(luò)應(yīng)用都默認(rèn)或作為用戶可配置選項(xiàng)使用安全協(xié)議。同時(shí),我們使用Nginx解密傳入的SSL加密請(qǐng)求,然后把解碼后的HTTP請(qǐng)求分配給上游服務(wù)器。
代碼清單8-2展示了一個(gè)用于解密傳入的HTTPS請(qǐng)求的server塊,并使用代碼清單8-1中我們使用過(guò)的代理指令轉(zhuǎn)發(fā)解密后的通信。
代碼清單8-2 使用SSL的server塊
server {
listen 443;
ssl on;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/cert.key;
default_type application/octet-stream;
location /static/ {
root /var/www/static;
if ($query_string) {
expires max;
}
}
location = /favicon.ico {
rewrite (.*) /static/favicon.ico;
}
location / {
proxy_pass_header Server;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_pass http://tornadoes;
}
}
這段代碼和上面的配置非常相似,除了Nginx將在標(biāo)準(zhǔn)HTTPS的443端口監(jiān)聽(tīng)安全Web請(qǐng)求外。如果你想強(qiáng)制使用SSL連接,你可以在server塊中包含一個(gè)rewrite指令來(lái)監(jiān)聽(tīng)80端口的HTTP連接。代碼清單8-3是這種重定向的一個(gè)例子。
代碼清單8-3 用于重定向HTTP請(qǐng)求到安全渠道的server塊
server {
listen 80;
server_name example.com;
rewrite /(.*) https://$http_host/$1 redirect;
}
Nginx是一個(gè)非常魯棒的工具,我們?cè)谶@里僅僅接觸到幫助Tornado部署的一些簡(jiǎn)單的配置選項(xiàng)。Nginx的wiki文檔是獲得安裝和配置這個(gè)強(qiáng)有力的工具額外信息的一個(gè)非常好的資源。
正如8.2節(jié)中埋下的伏筆,我們將在我們的Tornado應(yīng)用中運(yùn)行多個(gè)實(shí)例以充分利用現(xiàn)代的多處理器和多核服務(wù)器架構(gòu)。開(kāi)發(fā)團(tuán)隊(duì)大多傳聞每個(gè)核運(yùn)行一個(gè)Tornado進(jìn)程。但是,正如我們所知道的,大量的傳聞并不代表事實(shí),所以你的結(jié)果可能不同。在本節(jié)中,我們將討論在UNIX系統(tǒng)中管理多個(gè)Tornado實(shí)例的策略。
到目前為止,我們都是在命令行中運(yùn)行Tornado服務(wù)器的,就像$ python main.py --port=8000
。但是,再擦河南刮起的生產(chǎn)部署中,這是不可管理的。因?yàn)槲覀優(yōu)槊總€(gè)CPU核心運(yùn)行一個(gè)獨(dú)立的Tornado進(jìn)程,因此有很多進(jìn)程需要監(jiān)控和控制。supervisor守護(hù)進(jìn)程可以幫助我們完成這個(gè)任務(wù)。
Supervisor的設(shè)計(jì)是每次開(kāi)機(jī)時(shí)啟動(dòng)其配置文件中列出的進(jìn)程。這里,我們將看到管理我們?cè)贜ginx配置文件中作為上游主機(jī)提到的四個(gè)Tornado實(shí)例的Supervisor配置。典型的supervisord.conf文件中包含了全局的配置指令,并加載conf.d目錄下的其他配置文件。代碼清單8-4展示了我們想啟動(dòng)的Tornado進(jìn)程的配置文件。
代碼清單8-4 tornado.conf
[group:tornadoes]
programs=tornado-8000,tornado-8001,tornado-8002,tornado-8003
[program:tornado-8000]
command=python /var/www/main.py --port=8000
directory=/var/www
user=www-data
autorestart=true
redirect_stderr=true
stdout_logfile=/var/log/tornado.log
loglevel=info
[program:tornado-8001]
command=python /var/www/main.py --port=8001
directory=/var/www
user=www-data
autorestart=true
redirect_stderr=true
stdout_logfile=/var/log/tornado.log
loglevel=info
[program:tornado-8002]
command=python /var/www/main.py --port=8002
directory=/var/www
user=www-data
autorestart=true
redirect_stderr=true
stdout_logfile=/var/log/tornado.log
loglevel=info
[program:tornado-8003]
command=python /var/www/main.py --port=8003
directory=/var/www
user=www-data
autorestart=true
redirect_stderr=true
stdout_logfile=/var/log/tornado.log
loglevel=info
為了Supervisor有意義,你需要至少包含一個(gè)program部分。在代碼清單8-4中,我們定義了四個(gè)程序,分別命名為tornado-8000到tornado-8003。program部分定義了Supervisor將要運(yùn)行的每個(gè)命令的參數(shù)。command的值是必須的,通常是帶有我們希望監(jiān)聽(tīng)的port參數(shù)的Tornado應(yīng)用。我們還為每個(gè)程序的工作目錄、有效用戶和日志文件定義了額外的設(shè)置;而把a(bǔ)utorestart和redirect_stderr設(shè)置為true是非常有用的。
為了一起管理所有的Tornado進(jìn)程,創(chuàng)建一個(gè)組是很有必要的。在這個(gè)例子的頂部,我們聲明了一個(gè)叫作tornadoes的組,并在其中列出了每個(gè)程序?,F(xiàn)在,當(dāng)我們想要管理我們的Tornado應(yīng)用時(shí),我們可以通過(guò)帶有通配符的組名引用所有的組成程序。比如,要重啟應(yīng)用時(shí),我們只需要在supervisorctl工具中使用命令restart tornadoes:*
。
一旦你安裝和配置好Supervisor,你就可以使用supervisorctl來(lái)管理supervisord進(jìn)程。為了啟動(dòng)你的Web應(yīng)用,你可以讓Supervisor重新讀取配置,然后任何配置改變的程序或程序組將被重啟。你同樣可以手動(dòng)啟動(dòng)、停止和重啟被管理的程序或檢查整個(gè)系統(tǒng)的狀態(tài)。
supervisor> update
tornadoes: stopped
tornadoes: updated process group
supervisor> status
tornadoes:tornado-8000 RUNNING pid 32091, uptime 00:00:02
tornadoes:tornado-8001 RUNNING pid 32092, uptime 00:00:02
tornadoes:tornado-8002 RUNNING pid 32093, uptime 00:00:02
tornadoes:tornado-8003 RUNNING pid 32094, uptime 00:00:02
Supervisor和你系統(tǒng)的初始化進(jìn)程一起工作,并且它應(yīng)該在系統(tǒng)啟動(dòng)時(shí)自動(dòng)注冊(cè)守護(hù)進(jìn)程。當(dāng)supervisor啟動(dòng)后,程序組會(huì)自動(dòng)在線。默認(rèn)情況下,Supervisor會(huì)監(jiān)控子進(jìn)程,并在任何程序意外終止時(shí)重生。如果你想不管錯(cuò)誤碼,重啟被管理的進(jìn)程,你可以設(shè)置autorestart為true。
Supervisor不只可以使管理多個(gè)Tornado實(shí)例更容易,還能讓你在Tornado服務(wù)器遇到意外的服務(wù)中斷后重新上線時(shí)泰然處之。
掃描下方二維碼或打開(kāi)微信搜一搜“w3cschool編程獅”關(guān)注公眾號(hào)回復(fù)關(guān)鍵詞【Python123】或者【Python資料包】免費(fèi)領(lǐng)取 Python 學(xué)習(xí)資料,包含軟件安裝包,電子書(shū)、思維導(dǎo)圖等
更多建議: