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

2022-03-11 13:00:45
本篇文章帶大家聊聊Nginx與php-fpm之間的通訊機制,希望對大家有所幫助!

什麼是CGI?

講Fastcgi之前需要先講CGI,CGI是為了保證web server傳遞過來的資料是標準格式的,它是一個協定。每種動態語言( PHP,Python 等)的程式碼檔案需要通過對應的解析器才能被伺服器識別,而 CGI 協定就是用來使直譯器與伺服器可以互相通訊。PHP 檔案在伺服器上的解析需要用到 PHP 直譯器,再加上對應的 CGI 協定,從而使伺服器可以解析到 PHP 檔案。

1.jpeg

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 掉程序,而是繼續處理多個請求,這樣就大大提高了效率。

2.png

什麼是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就不能正常工作。

3.jpeg

因此就可以把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方式啟動的效果圖

4.jpeg

2、unix方式啟動的效果圖

5.jpeg

注意在演示的過程中遇到一個問題就是提示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與php-fpm間的通訊機制的詳細內容,更多請關注TW511.COM其它相關文章!