nginx負載均衡和配置PHP-FPM
PHP開發基本上都知道兩種執行環境,分別是LNMP和LAMP。區別主要在N和A上,當然從細節上來區分,兩個P也有不一樣的地方。不談之前的淺淡理解,最近一個專案上要配合進行PHP伺服器指向的切換。發現A伺服器上安裝了apache,B伺服器上安裝了nginx,每次請求B伺服器的nginx,業務邏輯在A上執行。
這和我之前所瞭解到的不太一樣,一般來說apache和php通過mod_php方式執行,而要是用到nginx,往往就直接用了php-fpm。在一番檢視後發現,確實基本apache的mod_php進行PHP解析,通過訪問B返回了正確的指令碼執行結果,而直接訪問了A,發現也得到了正確的結果。區別僅在於ip地址和埠的差別。
開啟nginx的配置檔案nginx.conf發現類似如下一段資訊:
location /papa/ {
proxy_pass http://192.168.3.100/;
}
proxy模組相關配置引數很多,proxy_pass簡單來說就是做一遍HTTP請求轉發。暫時不去深究為什麼用了nginx,又用了apache。個人認為這種方式存在不足之處,nginx一直以來以其靜態處理效能強勁聞名(當然現在動態處理能力也很強),通過配置location匹配抓取訪問URL指定欄位,再轉發請求到apache伺服器。可以看出來動態和靜態請求都全部推給了apache,那麼nginx僅僅就是做了個轉發,HTTP轉發這種事情,總會存在損耗。完全沒有利用起來nginx強勁的效果,唯一的好處可能就是做了負載均衡,方便佈置叢集。
第一想聊聊,基於壓測,要求部署兩套PHP的執行環境。
location /papa/ {
proxy_pass http://test_common/;
}
upstream test_common {
server 127.0.0.1;
}
proxy_pass 配置轉發請求地址,upstream配置多個server地址,且功能真的強大,舉幾個簡單的例子。
普通輪詢模式:按照1:1的比例輪詢伺服器執行指令碼
upstream test_common {
server 192.168.0.1;
server192.168.0.2;
}
權重輪詢模式:按照設定的比例輪詢伺服器執行指令碼
upstream test_common { server 192.168.0.1 weight=3; server192.168.0.2 weight=2; }
IP分配模式:根據請求的IP分配執行伺服器,保證相同IP在一個伺服器上執行指令碼。
之前在釋出到線上的時候,由於部署了兩套伺服器,導致基於SESSION的部分,出現問題,現在來看,一句話就能搞定。
upstream test_common {
ip_hash;
server 192.168.0.1 weight=3;
server192.168.0.2 weight=2;
}
重啟nginx,一訪問報了個400的錯誤。
改動nginx.conf配置檔案,訪問出現400異常,那麼暫時認定配置檔案的改動問題,根據頁面報錯資訊查錯,過程比較持久和繁雜,也沒有能解決的方法。
後來想想,繼續是nginx配置後出錯了,那就查查nginx的執行日誌吧,進入到nginx根目錄,執行tail -f logs/access.log
訪問一次後,發現新增一行
實際上這是apache的返回內容,進行apache日誌目錄,檢視日誌
誒,發現確實有點搞頭了,居然是apache報的錯,結合之前的資料搜尋,有粗略提到頭部問題,就決定看看能不能抓到請求的頭部資訊來試試水了。
執行命令:tcpdump -i lo port 80 -s 1024 -l –A
Host是upstream定義的虛擬名稱,nginx.conf改回初始配置繼續抓包結果為:
同時設定apache日誌級別為debug後,定位日誌:
AH02415: [strict] Invalid host name'test_common', problem near: _commo
AH00550: Client sent malformed Host header:test_common
定位到是請求頭Host的問題,可以使用proxy_set_header來設定Host資訊
location /papa/ {
proxy_pass http://test_common/;
proxy_set_header Host $host;
}
修改如上配置,繼續除錯,
抓取頭部發現變更成了IP地址,同時請求執行不再報400錯誤。
根據結果驗證過程,查了下nginx負載均衡相關,發現確實都有設定Host一項。
要特別說明的是,線上及以上重現為apache2.4和PHP5.6版本。後續在另外apache2.2和PHP5.3版本上測試時,發現不新增proxy_set_header Host $host;指令碼執行成功,Host也確實異常。
apache2.4和apache2.2對HTTP請求Host的檢測規則可能不一樣?
兩個版本造成不同結果的原因點,沒找出來。
第二想聊聊nginx和php-fpm之間的配置。
php-fpm安裝比較方便,基於5.3.3以上的版本已經被PHP整合,因此編譯的時候加入--enable-fpm即可。
單純的執行.php檔案,配置非常方便,nginx.conf註釋的內容開啟簡單修改就能直接用。
不過日常開發的專案都是基於TP框架,當然laravel原理也一樣。隱藏了入口檔案index.php,同時訪問路由通過URL攜帶進行解析後執行。
if (!-e $request_filename) {
rewrite ^/(.*)$ /index.php/$2 last;
}
這個配置主要是為了對訪問URL進行重寫操作,如果不存在此檔案,在最前端寫入index.php,這和apache的.htaccess檔案功能一樣。last網上的說法是重新發起一個請求(這裡存在疑問,重新發起請求是指新的一個HTTP請求嗎?觀察執行日誌並沒有兩條請求,我個人感覺可以理解為,繼續匹配後續的location了),相對的break會停止對後續location的匹配。
在經過URL重寫後,要對php檔案繼續做location的匹配,當找到匹配,則傳送請求到php-fpm。
觀察nginx日誌,提示/index.php/tp/Index/index has been denied。暫不考慮其他的,這個tp和index.php位置就不對,而apache重寫的rewrite正則相似,但是能正確的寫入index.php。
新改動如下:
if (!-e $request_filename) {
rewrite ^/(.*?)/(.*)$ /$1/index.php/$2 last;
}
重新的URL是正確了,因為沒法使用域名來測試,域名包含了專案名,應該不會出現這種情況。
剩下就是對php檔案location部分改動了,預設是
location ~ \.php$ {
root html;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
include fastcgi_params;
}
剛開始想著對正則改改,如:~ \.php(.*)$。畢竟index.php後面攜帶了內容嘛,後來想想有點傻,實際上並不存在類似tp/index.php/Index/index檔案。
查詢配置
location ~ \.php($|/) {
include fastcgi_params;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
set $path_info "/";
set $real_script_name $fastcgi_script_name;
if ($fastcgi_script_name ~ "^(.+?\.php)(/.+)$") {
set $real_script_name $1;
set $path_info $2;
}
fastcgi_param SCRIPT_NAME $real_script_name;
fastcgi_param PATH_INFO $path_info;
fastcgi_param SCRIPT_FILENAME $document_root$real_script_name;
}
和預設配置區別在於除了轉發請求,還設定了指令碼檔名、PATH_INFO變數(業務執行檔案相關引數)、指令碼檔案絕對路徑。這三個變數都能通過SERVER變數列印觀察。
總結一下,其實對這一塊的梳理還挺亂的,不過算是瞭解了下nginx和其與php-fpm之間的配置關係。其本質是一個代理伺服器,在能夠高效處理靜態資源的同時,作為類似Web請求中轉站和php-fpm建立聯絡,以sockt方式通訊達到執行PHP檔案的目的。
另外配置引數挺多,所以還是好好看看官方文件來做到知根知底吧。