FastCGI 最后一篇,我們將學(xué)習(xí)完剩下的所有配置指令。在這里,錯誤處理還是單獨拿出來成為一個小節(jié)了,而剩下的內(nèi)容都放到其它中進行學(xué)習(xí)。不要感覺是其它的就沒用了,有些配置指令還是非常重要的哦,或許正好你現(xiàn)在的項目就能用上呢。
今天學(xué)習(xí)的內(nèi)容都是可以設(shè)置在 http、server、location 中的,有特殊情況的我會單獨說。
FastCGI 模塊的錯誤處理可不是 error_page 那種指定跳轉(zhuǎn)頁面什么的,當然,也有一個和它相關(guān)的配置,之前我們就學(xué)過了。其它大部分的配置,其實是假設(shè)萬一連接的后端服務(wù)器出現(xiàn)問題了該怎么辦的,這里也會牽涉到一點 upstream 相關(guān)的內(nèi)容,這一塊我們后面還會細說,主線還是先看看 FastCGI 模塊是怎么處理這些問題的吧。
指定在哪些情況下應(yīng)將請求傳遞給下一個服務(wù)器。
fastcgi_next_upstream error | timeout | invalid_header | http_500 | http_503 | http_403 | http_404 | http_429 | non_idempotent | off ...;
默認值是 error timeout; 每個參數(shù)的意思其實也比較明顯,就是遇到這些指定的情況時,要不要將請求轉(zhuǎn)發(fā)給下一個 upstream 中配置的 server 。
這個測試比較麻煩,我們需要再啟動一個 PHP-FPM ,然后在 Nginx 的 http 模塊下配置一下 upstream ,也就是服務(wù)器組,這個具體內(nèi)容我們會在后面服務(wù)器組模塊的學(xué)習(xí)中學(xué)到,這個模塊的內(nèi)容就是 Nginx 負載均衡的配置。
# 再啟動一個PHP,需要修改php-fpm.conf及php-fpm.d/www.conf里面的內(nèi)容,比如 listen 部分和 pid 部分
# 然后執(zhí)行一下 php-fpm 就可以了
# http 模塊下
upstream fcgicache {
server unix:/var/sock/php-fpm/www.sock;
server unix:/var/sock/php-fpm/www2.sock;
}
啟動多個 PHP-FPM 這里不多解釋了,不會的小伙伴可以查下資料或者到時候看視頻哈。然后再修改 Nginx 配置中 fastcgi_pass 指向這個新的服務(wù)器組。
fastcgi_pass fcgicache;
默認配置中的 error 這個選項,其實就已經(jīng)幫我們處理掉 502 這種連接問題了,比如說這時候我們 kill 掉一個 PHP-FPM 進程,但是程序依然可以正常響應(yīng),也就是說,一般的 502 這種連接錯誤是 error 處理的。那么要如何檢測其它的錯誤情況下 fastcgi_next_upstream 的效果呢?我們可以在 php 文件中進行修改。
// 3.php
echo posix_getpid();
if (posix_getpid() < 1648){
throw new Exception("錯了");
}
posix_getpid() 是 PHP 的 Posix 擴展中的函數(shù),如果沒有話可以自己裝一下。它可以獲取到我們當前執(zhí)行的進程 ID ,查看進程情況,
[root@localhost phpfpm2]# ps -ef | grep php
root 1640 1 0 09:47 ? 00:00:00 php-fpm: master process (/root/phpfpm2/php-fpm.conf)
www 1641 1640 0 09:47 ? 00:00:00 php-fpm: pool www
www 1642 1640 0 09:47 ? 00:00:00 php-fpm: pool www
www 1643 1640 0 09:47 ? 00:00:00 php-fpm: pool www
www 1644 1640 0 09:47 ? 00:00:00 php-fpm: pool www
www 1645 1640 0 09:47 ? 00:00:00 php-fpm: pool www
root 1648 1 0 09:47 ? 00:00:00 php-fpm: master process (/etc/php-fpm.conf)
www 1649 1648 0 09:47 ? 00:00:00 php-fpm: pool www
www 1650 1648 0 09:47 ? 00:00:00 php-fpm: pool www
www 1651 1648 0 09:47 ? 00:00:00 php-fpm: pool www
www 1652 1648 0 09:47 ? 00:00:00 php-fpm: pool www
www 1653 1648 0 09:47 ? 00:00:00 php-fpm: pool www
可以看到兩個 php-fpm 進程的情況,上面 PHP 代碼的意思就是小于 php-fpm: master process (/etc/php-fpm.conf)
這個進程 ID 的進程,也就是另外一個 PHP-FPM 的進程處理時,都拋出一個異常。直接拋出異常就是 500 錯誤,500 錯誤是需要單獨的 http_500 來配置的。
在沒有配置 fastcgi_next_upstream http_500
的情況下,刷新頁面,會一下 500 ,一下正常,這是因為 upstream 默認情況下是輪詢的,這個以后我們也會細說?,F(xiàn)在,配置上 fastcgi_next_upstream 。
fastcgi_next_upstream error http_500;
再次不停地刷新,頁面始終會返回 200 ,而錯誤日志中,會有 FastCGI 的錯誤信息。
2022/08/25 10:07:31 [error] 1720#0: *110 FastCGI sent in stderr: "PHP message: PHP Fatal error: Uncaught Exception: 錯了
……………………
注意,這個配置項不是追加的,比如上面如果我們只設(shè)置了 http_500 ,那么之前說的 kill 掉某個 PHP-FPM 轉(zhuǎn)發(fā)跳轉(zhuǎn)的效果就沒有了。大家可以自己試試哦。
應(yīng)該記住,只有在尚未向客戶端發(fā)送任何內(nèi)容的情況下,才有可能將請求傳遞給下一個服務(wù)器。也就是說,如果在傳輸響應(yīng)的過程中發(fā)生錯誤或超時,則無法解決此問題。該指令還定義了與服務(wù)器通信的不成功嘗試。錯誤、超時和 invalid_header 的情況總是被認為是不成功的嘗試,即使它們沒有在指令中指定。 http_500、http_503 和 http_429 的情況僅在指令中指定時才被視為不成功的嘗試。 http_403 和 http_404 的情況永遠不會被認為是不成功的嘗試。將請求傳遞到下一個服務(wù)器可能會受到嘗試次數(shù)和時間的限制。
最后,有啥用?其實通過這個,就可以實現(xiàn) PHP-FPM 的負載均衡,只要有一個 PHP-FPM 存在,服務(wù)就可以一直提供,PHP-FPM 使用 TCP 端口形式也是可以分布到不同的主機或者 Docker 中的,并且可以實現(xiàn)不同的版本或者版本的平滑升級。
限制可以將請求傳遞到下一個服務(wù)器的時間。
fastcgi_next_upstream_timeout time;
默認 0 表示關(guān)閉此限制。
限制將請求傳遞到下一個服務(wù)器的可能嘗試次數(shù)。
fastcgi_next_upstream_tries number;
默認是 0 ,表示不限制。
設(shè)置要在從 FastCGI 服務(wù)器接收到的響應(yīng)的錯誤流中搜索的字符串。
fastcgi_catch_stderr string;
如果找到指定的字符串,則認為 FastCGI 服務(wù)器返回了無效響應(yīng)。這允許在 nginx 中處理應(yīng)用程序錯誤,例如:
location /php/ {
fastcgi_pass backend:9000;
...
fastcgi_catch_stderr "PHP Fatal error";
fastcgi_next_upstream error timeout invalid_header;
}
其實呀,就是我們上面學(xué)習(xí)過的 fastcgi_next_upstream ,只不過不是根據(jù)狀態(tài)碼,如果在響應(yīng)字符串中有相應(yīng)的內(nèi)容被捕獲到了,就直接走 fastcgi_next_upstream 的處理了。由于我們都是在一臺機器上的 PHP-FPM ,一個輸出錯誤了別的也是輸出錯誤,也就測不出什么效果,有興趣的小伙伴可以使用跨服務(wù)器的 IP Socket 方式連接 PHP 進行測試。
確定代碼大于或等于 300 的 FastCGI 服務(wù)器響應(yīng)是否應(yīng)傳遞給客戶端或被攔截并重定向到 nginx 以使用 error_page 指令進行處理。
fastcgi_intercept_errors on | off;
默認值是 off 。之前在學(xué)習(xí) error_page 時用過,也介紹過啦。注意,fastcgi_next_upstream 如果同時存在,并且 fastcgi_next_upstream 里面有正常響應(yīng)的,那么會走正常響應(yīng)的,如果所有的 upsteam 都返回錯誤,就會按這邊的配置。
剩下的就是一些不太好劃分大類的配置指令了。說實話,很多聞所未聞,根本就沒見過,而且平常配置的時候可能真的不太用得上。但是,說實話,很漲知識哦,特別是 PATH INFO 部分,我也是邊查資料邊學(xué)習(xí)的,以前還以為這只是 TP 特有的東西,原來是 TP 遵循的 PATH INFO 規(guī)范。怎么樣,感興趣了吧?別急,咱們一個一個來看。
定義與 FastCGI 服務(wù)器建立連接的超時時間。
fastcgi_connect_timeout time;
默認是 60s ,通常不超過 75 秒。注意,它是 Nginx 和 PHP-FPM 建立通訊的時間,不是說我們們在 PHP 中 sleep() 一下就可以測出來的。是 TCP 建立連接的超時時間。
啟用對來自 FastCGI 服務(wù)器的緩存和未緩存響應(yīng)的字節(jié)范圍支持,無論這些響應(yīng)中的“Accept-Ranges”字段如何。
fastcgi_force_ranges on | off;
默認值是 off ,具體作用不清楚,有了解的小伙伴可以評論留言哦。
與 FastCGI 服務(wù)器的傳出連接源自具有可選端口 (1.11.2) 的指定本地 IP 地址。
fastcgi_bind address [transparent] | off;
看不懂上面的解釋吧?我也看不懂,于是查了一下,它是指定 Nginx 與 FastCGI 通信的 IP 地址,一般不做設(shè)置。指令在調(diào)用 connect() 函數(shù)之前將解析每個上游 socket 到一個本地地址,可以使用在主機擁有多個網(wǎng)卡接口或別名的情況下,但是你只允許到外部的連接來自指定的網(wǎng)卡或者地址的情況下。 與回環(huán) IP 地址通信,需要設(shè)置路由表。
好嘛,一般不做設(shè)置,而且還是看不懂,更重要的是,我不知道咋測。就這樣吧,具體的解釋官方文檔上還有不少,不過純英文的。查到的解釋大部分也就是上面那一句話,那么我也重復(fù)我之前經(jīng)常說過的話,不懂就別瞎設(shè)置,期待有大佬用過或者明白啥意思的能夠用更通俗的話在評論里解釋一下,讓大家一起學(xué)習(xí)下哈。
確定當客戶端關(guān)閉連接而不等待響應(yīng)時是否應(yīng)關(guān)閉與 FastCGI 服務(wù)器的連接。
fastcgi_ignore_client_abort on | off;
默認是關(guān)閉的。
默認情況下,F(xiàn)astCGI 服務(wù)器將在發(fā)送響應(yīng)后立即關(guān)閉連接。
fastcgi_keep_conn on | off;
當這個指令設(shè)置為 on 時,nginx 將指示 FastCGI 服務(wù)器保持連接打開。這對于到 FastCGI 服務(wù)器的 keepalive 連接來說是必要的。我們之前學(xué)習(xí)過 Nginx 在處理和客戶端的連接時的長連接問題,對于和 FastCGI 的通信,也是可以通過長連接進行連接的。
限制從 FastCGI 服務(wù)器讀取響應(yīng)的速度。
fastcgi_limit_rate rate;
默認 0 表示不限制 ,速率以每秒字節(jié)數(shù)指定。該限制是針對每個請求設(shè)置的,因此如果 nginx 同時打開兩個到 FastCGI 服務(wù)器的連接,則總體速率將是指定限制的兩倍。僅當啟用了來自 FastCGI 服務(wù)器的響應(yīng)緩沖時,該限制才有效。和 Nginx 普通的 limit_rate 參數(shù)是一樣的,只不過這是針對后端服務(wù)器的。
指示是否將原始請求正文傳遞給 FastCGI 服務(wù)器。
fastcgi_pass_request_body on | off;
默認值是 on ,關(guān)了之后在 PHP 這邊就拿不到 $_POST
里的東西啦!
指示是否將原始請求的標頭字段傳遞給 FastCGI 服務(wù)器。
fastcgi_pass_request_headers on | off;
默認值是 on ,和上面的一樣,關(guān)了 PHP 中就拿不到 header 里面的內(nèi)容了。這個可以直接打印 $_SERVER
查看。
啟用或禁用客戶端請求正文的緩沖。
fastcgi_request_buffering on | off;
默認值是 on ,表示在將請求發(fā)送到 FastCGI 服務(wù)器之前,會從客戶端讀取整個請求正文。
當緩沖被禁用時,請求正文在收到后立即發(fā)送到 FastCGI 服務(wù)器。在這種情況下,如果 nginx 已經(jīng)開始發(fā)送請求正文,則無法將請求傳遞給下一個服務(wù)器。
貌似和 fastcgi_next_upstream 有關(guān)系,但是不知道怎么測試,有了解的小伙伴評論留言哈。
如果該指令設(shè)置為非零值,nginx 將嘗試使用 kqueue 方法的 NOTE_LOWAT 標志或 SO_SNDLOWAT 套接字選項,使用指定的大小來最小化到 FastCGI 服務(wù)器的傳出連接上的發(fā)送操作數(shù)。
fastcgi_send_lowat size;
好巧不巧,它的默認值就是 0 ,意思也就是不開啟的,具體功能和如何測試我也不太清楚,反正不懂就不要設(shè)置了,有了解這個配置的小伙伴評論留言哦。
設(shè)置將請求傳輸?shù)?FastCGI 服務(wù)器的超時時間。
fastcgi_send_timeout time;
默認值是 60s ,超時僅在兩個連續(xù)的寫操作之間設(shè)置,而不是為整個請求的傳輸設(shè)置。如果 FastCGI 服務(wù)器在這段時間內(nèi)沒有收到任何內(nèi)容,則關(guān)閉連接。
為到 FastCGI 服務(wù)器的傳出連接配置“TCP keepalive”行為。
fastcgi_socket_keepalive on | off;
它的默認值是 off ,在默認情況下,操作系統(tǒng)的設(shè)置對套接字有效。如果該指令設(shè)置為值“on”,則為套接字打開 SO_KEEPALIVE 套接字選項。好吧,又是網(wǎng)絡(luò)基礎(chǔ)知識中的,不知道咋測,但很明顯,它是是否在和 FastCGI 綁定的后端之間進行通信的 TCP 是否啟用長連接的一個設(shè)置。這個長連接和 HTTP 的長連接的概念是相同的,無非就是減少建立連接時的消耗從而提升性能。但是不懂就別亂動,保持默認就好,同時還是更希望有接觸過的小伙伴可以來釋疑一下哈。
網(wǎng)上能夠搜到同時開啟 fastcgi_socket_keepalive 和 fastcgi_keep_conn 之后會產(chǎn)生一些問題,可能的解決方案是要將 PHP-FPM 的 pm.max_requests 設(shè)置成和 keepalive_requests 相同的值。不過我沒遇到過,也不知道真假哈,先記錄一下,萬一將來遇到了就可以試試,可能的報錯信息是這樣的:
readv() failed (104: Connection reset by peer) while reading upstream and recv() failed (104: Connection reset by peer) while reading response header from upstream
定義一個捕獲 $fastcgi_path_info
變量值的正則表達式。
fastcgi_split_path_info regex;
正則表達式應(yīng)該有兩個捕獲:第一個成為 $fastcgi_script_name
變量的值,第二個成為 $fastcgi_path_info
變量的值。
之前我們學(xué)過了 $fastcgi_script_name
是干嘛的,有點像靜態(tài)頁面中的 $uri
變量,返回的都是訪問的路徑。不過針對 FastCGI ,還提供了一個變量 $fastcgi_path_info
,可以用于實現(xiàn) PATH INFO 能力。這個東西其實是 PHP 用得比較多的,通過 PATH INFO 實現(xiàn)偽靜態(tài)。最常見的就是 TP 框架的 http://xxxx.com/index.php/xxx/xx 這種路徑。不過一般我們會通過 Nginx 的 rewrite 重寫去掉中間的 index.php 。最后的請求 URL 就是 http://xxxx.com/xxx/xx 這樣的。
我們可以這樣配置一個。
fastcgi_split_path_info ^(.+\.php)(.*)$;
return 200 $uri+++$fastcgi_script_name+++$fastcgi_path_info;
然后請求配置了這個參數(shù)的路徑,比如這里是 http://192.168.56.88/fastcgi4/5.php/ppp/hhh 就會看到返回的結(jié)果中 $fastcgi_path_info
變量的值是 /ppp/hhh 。默認情況下它是沒有值的,如果想把這個值傳給 PHP 的話,就可以加一個 fastcgi_param 。
fastcgi_param PATH_INFO $fastcgi_path_info;
這樣在 PHP 中,就可以在 $_SERVER
中看到 PATH_INFO 這個屬性了。
在 TP6 中,如果使用 fastcgi_split_path_info ,那么就可以不用再配置官網(wǎng)那個 rewrite 的方式來實現(xiàn) PATH INFO 路徑了。
location / { // …..省略部分代碼
if (!-e $request_filename) {
rewrite ^(.*)$ /index.php?s=/$1 last;
}
}
官網(wǎng)這個配置其實是對于不支持 PATH INFO 的服務(wù)器,比如老版本的 Nginx 而采用的一種跳轉(zhuǎn)回兼容模式的方式來實現(xiàn)的 PATH INFO 效果。同時還可以去除 index.php 。我們不使用它,直接使用下面這個配置。
location ~ \.php {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_split_path_info ^(.+\.php)(.*)$;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
注意,要把 location 默認正則中的最后一個 $
去掉哦,或者換成 \.php(.*)$
之前默認的是 location ~ \.php$
這個樣子的。然后就可以直接通過完全的 PATH INFO 來使用 TP 了,不需要再配官方文檔那個 rewrite 的指令。不過,需要注意的是 PATH INFO 是有 index.php 的,要想去除 index.php ,還是得 rewrite ,所以,說實話,還是老實用之前那種形式吧。但如果啟用了 PATH INFO ,則 rewrite 可以換個寫法 rewrite ^(.*)$ /index.php$1 last;
,因為我們不需要使用 TP 的兼容模式了,也就不需要那個名為 s 的 GET 參數(shù)了。
而對于 Laravel 框架來說,則走的不是 PATH INFO 模式,它是根據(jù) $request_uri
通過 fastcgi_param 傳遞到 PHP 的 $_SERVER
中的 REQUEST_URI 來進行路由解析分析的,所以它在 Nginx 的配置中,rewrite 只需要指向 /index.php 就可以了,不需要像 TP 那樣還要帶個 s 參數(shù)。Laravel 是這樣的:
location / {
if (!-e $request_filename) {
rewrite ^(.*)$ /index.php last;
}
}
也就是說,使用 Laravel 時不用考慮 PATH INFO 的問題。
允許將文件保存到磁盤。
fastcgi_store on | off | string;
默認值是 off ,on 參數(shù)保存具有與指令別名或根目錄相對應(yīng)的路徑的文件。 off 參數(shù)禁用保存文件。此外,可以使用帶有變量的字符串顯式設(shè)置文件名。
根據(jù)收到的“Last-Modified”響應(yīng)頭域設(shè)置文件的修改時間。響應(yīng)首先被寫入一個臨時文件,然后文件被重命名。從版本 0.8.9 開始,臨時文件和持久存儲可以放在不同的文件系統(tǒng)上。但是,請注意,在這種情況下,文件是跨兩個文件系統(tǒng)復(fù)制的,而不是廉價的重命名操作。因此建議對于任何給定的位置,保存的文件和保存臨時文件的目錄(由 fastcgi_temp_path 指令設(shè)置)放在同一個文件系統(tǒng)上。
該指令可用于創(chuàng)建靜態(tài)不可更改文件的本地副本。
簡單按照官網(wǎng)的例子試了下,好像沒啥明顯的效果,去百度、Google也沒找到有啥資料,只有一篇文章說不要開,會影響性能。好吧,還是希望有相關(guān)經(jīng)驗的小伙伴評論留言一起學(xué)習(xí)哦!
為新創(chuàng)建的文件和目錄設(shè)置訪問權(quán)限。
fastcgi_store_access users:permissions ...;
默認值是 user:rw ,如果指定了任何組或所有訪問權(quán)限,則可以省略用戶權(quán)限。
錯誤處理中我們見到了 FastCGI 也是可以做負載均衡的,說實話,帶 pass 這個詞的,在 Nginx 中其實都可以做負載均衡,因為它們其實都是一個意思,通過(代理)到某個地方。最后在其它部分,PATH INFO 這一塊的知識對我來說確實是收獲非常大,別看干了這么久,而且也用過 TP ,但直到今天才真的明白 TP 文檔中關(guān)于 URL 路徑這里講的是啥意思,慚愧啊。
好了,到此為止,整個 FastCGI 相關(guān)的模塊就學(xué)習(xí)完了。怎么樣,之前說過的學(xué)習(xí) Nginx 就是在挑戰(zhàn)我們的基礎(chǔ)知識水平,這話一點都沒錯吧。很多東西我也不知道,但是學(xué)習(xí)文檔,特別是寫文章的時候,為了要表述清楚,就需要查詢更多的相關(guān)資料,自然而然地順帶學(xué)習(xí)到了更多的知識。而且,這樣學(xué)習(xí)的效果更好。之前在小視頻和一些文章中也提過,這就是費曼學(xué)習(xí)法,堅持下去就是最大的收獲,大家也可以嘗試一下哦。
最后再強調(diào)一遍,uwsgi 和 FastCGI 的很多內(nèi)容很像,后面也不會單獨再寫 uwsgi 部分的內(nèi)容了,有 Python 同學(xué)或者其它語言的同學(xué)使用 uwsgi 的可以自己參考一下官方文檔進行學(xué)習(xí)。
參考文檔:
http://nginx.org/en/docs/http/ngx_http_fastcgi_module.html