一文淺析Nginx與php-fpm間的通信機制

本篇文章帶大家聊聊nginx與php-fpm之間的通信機制,希望對大家有所幫助!

一文淺析Nginx與php-fpm間的通信機制

什么是CGI?

講Fastcgi之前需要先講CGI,CGI是為了保證web server傳遞過來的數據是標準格式的,它是一個協議。每種動態語言( PHP,Python 等)的代碼文件需要通過對應的解析器才能被服務器識別,而 CGI 協議就是用來使解釋器與服務器可以互相通信。PHP 文件在服務器上的解析需要用到 PHP 解釋器,再加上對應的 CGI 協議,從而使服務器可以解析到 PHP 文件。

一文淺析Nginx與php-fpm間的通信機制

立即學習PHP免費學習筆記(深入)”;

1.用戶通過客戶端(瀏覽器)輸入一個網址,例如www.baidu.com。

2.瀏覽器經過一些列的處理(這里省略其中的流程),請求到對應服務器的。

3.服務器網卡根據監聽到的端口,將請求發送給對應的軟件服務。

4.web服務器(nginx/Apache等web軟件)接收請求后,通過fast-cgi或者cgi協議,將請求數據轉發給php-fpm進程管理器。php-fpm將任務下發給下面空閑的work進程,此時work進程中的php解釋器開始處理文件。

5.php解釋器處理好,通過fast-cgi或者cgi協議,再將轉換后的數據返給web服務器,web服務器再響應給客戶端。

什么是FastCGI?

Fastcgi是CGI的更高級的一種方式,是用來提高CGI程序性能的。由于 CGI 的機制是每處理一個請求需要 fork 一個 CGI 進程,請求結束再kill掉這個進程,在實際應用上比較浪費資源,于是就出現了CGI 的改良版本 FastCGI,FastCGI 在請求處理完后,不會 kill 掉進程,而是繼續處理多個請求,這樣就大大提高了效率。

一文淺析Nginx與php-fpm間的通信機制

什么是php-fpm?

PHP-FPM 即 PHP-FastCGI Process Manager, 它是 FastCGI 的實現,并提供了進程管理的功能。進程包含 master 進程和 worker 進程兩種;master 進程只有一個,負責監聽端口,接收來自服務器的請求,而 worker 進程則一般有多個(具體數量根據實際需要進行配置),每個進程內部都會嵌入一個 PHP 解釋器,是代碼真正執行的地方。在沒有php-fpm之前,每當我們修改了php.ini的配置信息,都會面臨著下面的幾個問題:

  • 需要重啟php-cgi程序,才能使配置文件生效,同時php-cgi不支持平滑重啟。

  • kill掉php-cgi程序時,必須重新啟動,否則PHP就不能正常工作。

一文淺析Nginx與php-fpm間的通信機制

因此就可以把php-fpm理解為,是一個實現了Fastcgi協議的程序,用來管理Fastcgi啟動的進程的,即能夠調度php-cgi進程的程序。現已在PHP內核中就集成了PHP-FPM,使用–enalbe-fpm這個編譯參數即可。另外,修改了php.ini配置文件后,沒辦法平滑重啟,需要重啟php-fpm才可。此時新fork的worker會用新的配置,已經存在的worker繼續處理完手上的活。

Web服務器與程序解析器運行流程(Nginx與php-fpm通信機制(通信流程))

web server(如nginx)只是內容的分發者。比如,如果請求/index.html,那么web server會去文件系統中找到這個文件,發送給瀏覽器,這里分發的是靜態資源。如果現在請求的是/index.php,根據配置文件,nginx知道這個不是靜態文件,需要去找PHP解析器來處理,那么他會把這個請求簡單處理后交給PHP解析器。此時CGI便是規定了要傳什么數據/以什么格式傳輸給php解析器的協議。當web server收到/index.php這個請求后,會啟動對應的CGI程序,這里就是PHP的解析器。接下來PHP解析器會解析php.ini文件,初始化執行環境,然后處理請求,再以CGI規定的格式返回處理后的結果,退出進程。

CGI與FastCGI相比較

兩者的主要差距在于性能瓶頸。前者接受到一個請求就會fork一個進程,后者則是事先啟動一部分的worker進程。

CGI工作原理

CGI針對每個http請求都是fork一個新進程來進行處理,接著讀取php.ini文件配置信息,初始化執行環境等。

2.然后這個進程會把處理完的數據返回給web服務器,最后web服務器把內容發送給用戶。

3.剛才fork的進程也隨之退出。

4.如果下次用戶還請求動態資源,那么web服務器又再次fork一個新進程,周而復始的進行。

FastCGI工作原理

Fastcgi則會先fork一個master,解析配置文件,初始化執行環境,然后再fork多個worker。

2.當請求過來時,master會傳遞給一個worker,然后立即可以接受下一個請求。這樣就避免了重復的勞動,效率自然是高。

3.而且當worker不夠用時,master可以根據配置預先啟動幾個worker等著;當然空閑worker太多時,也會停掉一些,這樣就提高了性能,也節約了資源。這就是Fastcgi的對進程的管理。大多數Fastcgi實現都會維護一個進程池。

注:swoole作為httpserver,實際上也是類似這樣的工作方式。

Nginx與php-fpm通信分析

Nginx與php-fpm通信有兩種方式,一種是通過tcp socket和 unix socket。前者是通過ip:端口方式進行通信,后者是通過php啟動生成的socket文件進行通信。因此tcp socket的方式可以將兩者分布再不同的機器上,只要Nginx能夠連接到php服務器的端口即可。后者的方式,是統一主機上進行通訊的方式,因此兩者只能再同一臺機器上面。

tcp socket和 unix socket兩者的優缺點

由于 Unix socket 不需要經過網絡協議棧,不需要打包拆包、計算校驗和、維護序號和應答等,只是將應用層數據從一個進程拷貝到另一個進程。所以其效率比 tcp socket 的方式要高,可減少不必要的 tcp 開銷。不過,unix socket 高并發時不穩定,連接數爆發時,會產生大量的長時緩存,在沒有面向連接協議的支撐下,大數據包可能會直接出錯不返回異常。而 tcp 這樣的面向連接的協議,可以更好的保證通信的正確性和完整性。

如何選擇tcp socket與unix socket

1.由于tcp方式相對unix的方式,并發量更高,因此針對并發量高的項目,建議采用tcp方式,現在Nginx配置示例文件默認的也是tcp方式。

2.使用unix方式,可以優化的點,就是將socket文件放在/dev/shm目錄下面,至于為什么放在這個目錄可以參考.https://www.linuxidc.com/Linux/2014-05/101818.htm。大致的意思,就是該目錄下面的文件是不是存儲再硬盤中的,而是存儲再內存中的。至于硬盤讀取和內存讀取,誰快誰慢,肯定是內存最快了。

3.使用unix方式可以使用backlog,backlog的介紹,可以參考該文章。https://blog.csdn.net/raintungli/article/details/37913765。具體的配置可以參考下面。nginx 配置:

server?{ ??listen?80?default?backlog?=?100; ??}

php-fpm 配置:

listen.backlog?=?1000
配置示例

Nginx與PHP通信的方式,取決于PHP啟動的方式,下面直接演示兩種方式的配置文件,后面記錄PHP啟動方式的配置 1.tcp socket

server?{ ????????listen???????80; ????????server_name??laravel_demo.com; ????????charset?utf-8; ????????access_log??logs/laravel_demo.com.access.log; ????????root???/usr/local/var/www/laravel64_demo/public/; ????????index??index.php?index.html?index.htm;  ????????error_page???500?502?503?504??/50x.html;  ????????location?/?{ ????????????if?(!-e?$request_filename)?{ ????????????????rewrite?^(.*)$??/index.php?s=$1?last; ????????????????break; ????????????} ????????} ????????###?此處就是Nginx與tcp?socket通信配置,這里部署的同一臺及其,因此IP就是127.0.0.1 ????????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; ????????????} ????}

2.unix socket

server?{ ????????listen???????80; ????????server_name??laravel_demo.com; ????????charset?utf-8; ????????access_log??logs/laravel_demo.com.access.log; ????????root???/usr/local/var/www/laravel64_demo/public/; ????????index??index.php?index.html?index.htm;  ????????error_page???500?502?503?504??/50x.html;  ????????location?/?{ ????????????if?(!-e?$request_filename)?{ ????????????????rewrite?^(.*)$??/index.php?s=$1?last; ????????????????break; ????????????} ????????} ????????###?此處就是Nginx與unix?socket通信配置,我的socket文件在/usr/run/目錄下 ????????location?~?.php$?{ ????????????????fasrcgi_pass?/usr/tmp/php-fpm.sock; ????????????????fastcgi_index??index.php; ????????????????fastcgi_param??SCRIPT_FILENAME??$document_root$fastcgi_script_name; ????????????????include????????fastcgi_params; ????????????} ????}

上面演示了具體的配置示例,下面演示的是PHP啟動方式的配置,這里需要和上面Nginx的配置一致,如果是tcp方式就采用tcp方式啟動,如果是unix方式采用socket文件的方式啟動。

#?tcp方式啟動 listen?=?127.0.0.1:9000 #?unix方式啟動 listen?=?/usr/tmp/php-fpm.sock;

采用上面的兩種方式中的任意一種,直接使用php-fpm方式啟動php即可。

1、tcp方式啟動的效果圖

一文淺析Nginx與php-fpm間的通信機制

2、unix方式啟動的效果圖

一文淺析Nginx與php-fpm間的通信機制

注意在演示的過程中遇到一個問題就是提示Nginx無法讀取php生成的unix socket文件。這中情況是因為權限組導致的。因此再php-fpm的配置配置文件中要設置權限組,同時Nginx也需要設置權限組,再很多的集成開發環境中已經配置好了,因此可以減少此步驟。下面就是示例 Nginx配置文件:

#配置php-fpm的用戶以及所屬用戶組 user??www?www;  worker_processes?auto;  error_log??/home/wwwlogs/nginx_error.log??crit;  pid????????/usr/local/nginx/logs/nginx.pid;  #Specifies?the?value?for?maximum?file?descriptors?that?can?be?opened?by?this?process. worker_rlimit_nofile?51200;

php-fpm配置文件(我們平常可能更多的是配置php.ini的文件,這里需要區分兩者之間的區別,php.ini是針對php的配置文件,可以簡單的理解為php再編譯源碼時會用到這里的配置,而關于php這個應用程序執行的情況就會用到php-fpm的配置文件):

;?配置php-fpm的用戶以及所屬用戶組 user?=?www group?=?www  ;?The?address?on?which?to?accept?FastCGI?requests. ;?Valid?syntaxes?are: ;???'ip.add.re.ss:port'????-?to?listen?on?a?TCP?socket?to?a?specific?IPv4?address?on ;????????????????????????????a?specific?port; ;???'[ip:6:addr:ess]:port'?-?to?listen?on?a?TCP?socket?to?a?specific?IPv6?address?on ;????????????????????????????a?specific?port; ;???'port'?????????????????-?to?listen?on?a?TCP?socket?to?all?addresses ;????????????????????????????(IPv6?and?IPv4-mapped)?on?a?specific?port; ;???'/path/to/unix/socket'?-?to?listen?on?a?unix?socket. ;?Note:?This?value?is?mandatory. ;listen?=?127.0.0.1:9000 listen?=?/usr/tmp/php-fpm.sock;

推薦教程:nginx教程

? 版權聲明
THE END
喜歡就支持一下吧
點贊12 分享