1. 程式人生 > >nginx負載均衡和配置PHP-FPM

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檔案的目的。

另外配置引數挺多,所以還是好好看看官方文件來做到知根知底吧。