日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > php >内容正文

php

CGI,FastCGI,PHP-CGI,PHP-FPM

發(fā)布時間:2024/1/17 php 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 CGI,FastCGI,PHP-CGI,PHP-FPM 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

CGI 簡介

CGI全稱是“通用網(wǎng)關(guān)接口”(Common Gateway Interface),它可以讓一個客戶端,從網(wǎng)頁瀏覽器向執(zhí)行在Web服務器上的程序請求數(shù)據(jù)。 CGI描述了客戶端和這個程序之間傳輸數(shù)據(jù)的一種標準。 CGI的一個目的是要獨立于任何語言的,所以CGI可以用任何一種語言編寫,只要這種語言具有標準輸入、輸出和環(huán)境變量。 如php,perl,tcl等。

CGI 的運行原理

  • 客戶端訪問某個 URL 地址之后,通過 GET/POST/PUT 等方式提交數(shù)據(jù),并通過 HTTP 協(xié)議向 Web 服務器發(fā)出請求。
  • 服務器端的 HTTP Daemon(守護進程)啟動一個子進程。然后在子進程中,將 HTTP 請求里描述的信息通過標準輸入 stdin 和環(huán)境變量傳遞給 URL 指定的 CGI 程序,并啟動此應用程序進行處理,處理結(jié)果通過標準輸出 stdout 返回給 HTTP Daemon 子進程。
  • 再由 HTTP Daemon 子進程通過 HTTP 協(xié)議返回給客戶端。
  • 上面的這段話理解可能還是比較抽象,下面我們就通過一次 GET 請求為例進行詳細說明。

    ?

    圖2.7 CGI 運行原理示舉例示意圖

    ?

    如圖所示,本次請求的流程如下:

  • 客戶端訪問?http://127.0.0.1:9003/cgi-bin/user?id=1
  • 127.0.0.1 上監(jiān)聽 9003 端口的守護進程接受到該請求
  • 通過解析 HTTP 頭信息,得知是 GET 請求,并且請求的是?/cgi-bin/?目錄下的?user?文件。
  • 將 uri 里的?id=1?通過存入?QUERY_STRING?環(huán)境變量。
  • Web 守護進程 fork 一個子進程,然后在子進程中執(zhí)行 user 程序,通過環(huán)境變量獲取到id。
  • 執(zhí)行完畢之后,將結(jié)果通過標準輸出返回到子進程。
  • 子進程將結(jié)果返回給客戶端。
  • FastCGI 簡介

    FastCGI是Web服務器和處理程序之間通信的一種協(xié)議, 是CGI的一種改進方案,FastCGI像是一個常駐(long-lived)型的CGI, 它可以一直執(zhí)行,在請求到達時不會花費時間去fork一個進程來處理(這是CGI最為人詬病的fork-and-execute模式)。 正是因為他只是一個通信協(xié)議,它還支持分布式的運算,所以 FastCGI 程序可以在網(wǎng)站服務器以外的主機上執(zhí)行,并且可以接受來自其它網(wǎng)站服務器的請求。

    FastCGI 是與語言無關(guān)的、可伸縮架構(gòu)的 CGI 開放擴展,將 CGI 解釋器進程保持在內(nèi)存中,以此獲得較高的性能。 CGI 程序反復加載是 CGI 性能低下的主要原因,如果 CGI 程序保持在內(nèi)存中并接受 FastCGI 進程管理器調(diào)度, 則可以提供良好的性能、伸縮性、Fail-Over 特性等。

    FastCGI 工作流程如下:

  • FastCGI 進程管理器自身初始化,啟動多個 CGI 解釋器進程,并等待來自 Web Server 的連接。
  • Web 服務器與 FastCGI 進程管理器進行 Socket 通信,通過 FastCGI 協(xié)議發(fā)送 CGI 環(huán)境變量和標準輸入數(shù)據(jù)給 CGI 解釋器進程。
  • CGI 解釋器進程完成處理后將標準輸出和錯誤信息從同一連接返回 Web Server。
  • CGI 解釋器進程接著等待并處理來自 Web Server 的下一個連接。
  • ?

    圖2.8 FastCGI 運行原理示舉例示意圖

    FastCGI 與傳統(tǒng) CGI 模式的區(qū)別之一則是 Web 服務器不是直接執(zhí)行 CGI 程序了,而是通過 Socket 與 FastCGI 響應器(FastCGI 進程管理器)進行交互,也正是由于 FastCGI 進程管理器是基于 Socket 通信的,所以也是分布式的,Web 服務器可以和 CGI 響應器服務器分開部署。Web 服務器需要將數(shù)據(jù) CGI/1.1 的規(guī)范封裝在遵循 FastCGI 協(xié)議包中發(fā)送給 FastCGI 響應器程序。

    FastCGI 協(xié)議

    可能上面的內(nèi)容理解起來還是很抽象,這是由于第一對FastCGI協(xié)議還沒有一個大概的認識,第二沒有實際代碼的學習。所以需要預先學習下?FastCGI 協(xié)議,不一定需要完全看懂,可大致了解之后,看完本篇再結(jié)合著學習理解消化。

    下面結(jié)合 PHP 的 FastCGI 的代碼進行分析,不作特殊說明以下代碼均來自于 PHP 源碼。

    FastCGI 消息類型

    FastCGI 將傳輸?shù)南⒆隽撕芏囝愋偷膭澐?#xff0c;其結(jié)構(gòu)體定義如下:

    typedef enum _fcgi_request_type {FCGI_BEGIN_REQUEST = 1, /* [in] */ FCGI_ABORT_REQUEST = 2, /* [in] (not supported) */ FCGI_END_REQUEST = 3, /* [out] */ FCGI_PARAMS = 4, /* [in] environment variables */ FCGI_STDIN = 5, /* [in] post data */ FCGI_STDOUT = 6, /* [out] response */ FCGI_STDERR = 7, /* [out] errors */ FCGI_DATA = 8, /* [in] filter data (not supported) */ FCGI_GET_VALUES = 9, /* [in] */ FCGI_GET_VALUES_RESULT = 10 /* [out] */ } fcgi_request_type;

    消息的發(fā)送順序

    下圖是一個比較常見消息傳遞流程

    ?

    圖2.9 FastCGI 消息傳遞流程示意圖

    ?

    最先發(fā)送的是FCGI_BEGIN_REQUEST,然后是FCGI_PARAMS和FCGI_STDIN,由于每個消息頭(下面將詳細說明)里面能夠承載的最大長度是65535,所以這兩種類型的消息不一定只發(fā)送一次,有可能連續(xù)發(fā)送多次。

    FastCGI 響應體處理完畢之后,將發(fā)送FCGI_STDOUT、FCGI_STDERR,同理也可能多次連續(xù)發(fā)送。最后以FCGI_END_REQUEST表示請求的結(jié)束。 需要注意的一點,FCGI_BEGIN_REQUEST和FCGI_END_REQUEST分別標識著請求的開始和結(jié)束,與整個協(xié)議息息相關(guān),所以他們的消息體的內(nèi)容也是協(xié)議的一部分,因此也會有相應的結(jié)構(gòu)體與之對應(后面會詳細說明)。而環(huán)境變量、標準輸入、標準輸出、錯誤輸出,這些都是業(yè)務相關(guān),與協(xié)議無關(guān),所以他們的消息體的內(nèi)容則無結(jié)構(gòu)體對應。

    由于整個消息是二進制連續(xù)傳遞的,所以必須定義一個統(tǒng)一的結(jié)構(gòu)的消息頭,這樣以便讀取每個消息的消息體,方便消息的切割。這在網(wǎng)絡通訊中是非常常見的一種手段。

    FastCGI 消息頭

    如上,FastCGI 消息分10種消息類型,有的是輸入有的是輸出。而所有的消息都以一個消息頭開始。其結(jié)構(gòu)體定義如下:

    typedef struct _fcgi_header {unsigned char version; unsigned char type; unsigned char requestIdB1; unsigned char requestIdB0; unsigned char contentLengthB1; unsigned char contentLengthB0; unsigned char paddingLength; unsigned char reserved; } fcgi_header;

    字段解釋下:

    version標識FastCGI協(xié)議版本。?type?標識FastCGI記錄類型,也就是記錄執(zhí)行的一般職能。?requestId標識記錄所屬的FastCGI請求。?contentLength記錄的contentData組件的字節(jié)數(shù)。

    關(guān)于上面的xxB1和xxB0的協(xié)議說明:當兩個相鄰的結(jié)構(gòu)組件除了后綴“B1”和“B0”之外命名相同時,它表示這兩個組件可視為估值為B1<<8 + B0的單個數(shù)字。該單個數(shù)字的名字是這些組件減去后綴的名字。這個約定歸納了一個由超過兩個字節(jié)表示的數(shù)字的處理方式。

    比如協(xié)議頭中requestId和contentLength表示的最大值就是 65535。

    #include <stdio.h> #include <stdlib.h> #include <limits.h>int main() { unsigned char requestIdB1 = UCHAR_MAX; unsigned char requestIdB0 = UCHAR_MAX; printf("%d\n", (requestIdB1 << 8) + requestIdB0); // 65535 }

    你可能會想到如果一個消息體長度超過65535怎么辦,則分割為多個相同類型的消息發(fā)送即可。

    PHP中的CGI實現(xiàn)

    PHP的CGI實現(xiàn)了FastCGI協(xié)議,是一個TCP或UDP協(xié)議的服務器接受來自Web服務器的請求, 當啟動時創(chuàng)建TCP/UDP協(xié)議的服務器的socket監(jiān)聽,并接收相關(guān)請求進行處理。隨后就進入了PHP的生命周期: 模塊初始化,sapi初始化,處理PHP請求,模塊關(guān)閉,sapi關(guān)閉等就構(gòu)成了整個CGI的生命周期。

    以TCP為例,在TCP的服務端,一般會執(zhí)行這樣幾個操作步驟:

  • 調(diào)用socket函數(shù)創(chuàng)建一個TCP用的流式套接字;
  • 調(diào)用bind函數(shù)將服務器的本地地址與前面創(chuàng)建的套接字綁定;
  • 調(diào)用listen函數(shù)將新創(chuàng)建的套接字作為監(jiān)聽,等待客戶端發(fā)起的連接,當客戶端有多個連接連接到這個套接字時,可能需要排隊處理;
  • 服務器進程調(diào)用accept函數(shù)進入阻塞狀態(tài),直到有客戶進程調(diào)用connect函數(shù)而建立起一個連接;
  • 當與客戶端創(chuàng)建連接后,服務器調(diào)用read_stream函數(shù)讀取客戶的請求;
  • 處理完數(shù)據(jù)后,服務器調(diào)用write函數(shù)向客戶端發(fā)送應答。
  • TCP上客戶-服務器事務的時序如圖2.6所示:

    ?

    圖2.6 TCP上客戶-服務器事務的時序

    ?

    PHP的CGI實現(xiàn)從cgi_main.c文件的main函數(shù)開始,在main函數(shù)中調(diào)用了定義在fastcgi.c文件中的初始化,監(jiān)聽等函數(shù)。 對比TCP的流程,我們查看PHP對TCP協(xié)議的實現(xiàn),雖然PHP本身也實現(xiàn)了這些流程,但是在main函數(shù)中一些過程被封裝成一個函數(shù)實現(xiàn)。 對應TCP的操作流程,PHP首先會執(zhí)行創(chuàng)建socket,綁定套接字,創(chuàng)建監(jiān)聽:

    if (bindpath) {fcgi_fd = fcgi_listen(bindpath, 128); // 實現(xiàn)socket監(jiān)聽,調(diào)用fcgi_init初始化 ... }

    在fastcgi.c文件中,fcgi_listen函數(shù)主要用于創(chuàng)建、綁定socket并開始監(jiān)聽,它走完了前面所列TCP流程的前三個階段,

    if ((listen_socket = socket(sa.sa.sa_family, SOCK_STREAM, 0)) < 0 || ... bind(listen_socket, (struct sockaddr *) &sa, sock_len) < 0 || listen(listen_socket, backlog) < 0) { ... }

    當服務端初始化完成后,進程調(diào)用accept函數(shù)進入阻塞狀態(tài),在main函數(shù)中我們看到如下代碼:

    while (parent) {do { pid = fork(); // 生成新的子進程 switch (pid) { case 0: // 子進程 parent = 0; ? /* don't catch our signals */ sigaction(SIGTERM, &old_term, 0); // 終止信號 sigaction(SIGQUIT, &old_quit, 0); // 終端退出符 sigaction(SIGINT, &old_int, 0); // 終端中斷符 break; ... default: /* Fine */ running++; break; } while (parent && (running < children)); ? ... while (!fastcgi || fcgi_accept_request(&request) >= 0) { SG(server_context) = (void *) &request; init_request_info(TSRMLS_C); CG(interactive) = 0; ... }

    如上的代碼是一個生成子進程,并等待用戶請求。在fcgi_accept_request函數(shù)中,程序會調(diào)用accept函數(shù)阻塞新創(chuàng)建的進程。 當用戶的請求到達時,fcgi_accept_request函數(shù)會判斷是否處理用戶的請求,其中會過濾某些連接請求,忽略受限制客戶的請求, 如果程序受理用戶的請求,它將分析請求的信息,將相關(guān)的變量寫到對應的變量中。 其中在讀取請求內(nèi)容時調(diào)用了safe_read方法。如下所示:?[main() -> fcgi_accept_request() -> fcgi_read_request() -> safe_read()]

    static inline ssize_t safe_read(fcgi_request *req, const void *buf, size_t count) { size_t n = 0; do { ... // 省略 對win32的處理 ret = read(req->fd, ((char*)buf)+n, count-n); // 非win版本的讀操作 ... // 省略 } while (n != count); ? }

    如上對應服務器端讀取用戶的請求數(shù)據(jù)。

    在請求初始化完成,讀取請求完畢后,就該處理請求的PHP文件了。 假設此次請求為PHP_MODE_STANDARD則會調(diào)用php_execute_script執(zhí)行PHP文件。 在此函數(shù)中它先初始化此文件相關(guān)的一些內(nèi)容,然后再調(diào)用zend_execute_scripts函數(shù),對PHP文件進行詞法分析和語法分析,生成中間代碼, 并執(zhí)行zend_execute函數(shù),從而執(zhí)行這些中間代碼。關(guān)于整個腳本的執(zhí)行請參見第三節(jié) 腳本的執(zhí)行。

    在處理完用戶的請求后,服務器端將返回信息給客戶端,此時在main函數(shù)中調(diào)用的是fcgi_finish_request(&request, 1); fcgi_finish_request函數(shù)定義在fastcgi.c文件中,其代碼如下:

    int fcgi_finish_request(fcgi_request *req, int force_close) { int ret = 1; ? if (req->fd >= 0) { if (!req->closed) { ret = fcgi_flush(req, 1); req->closed = 1; } fcgi_close(req, force_close, 1); } return ret; }

    如上,當socket處于打開狀態(tài),并且請求未關(guān)閉,則會將執(zhí)行后的結(jié)果刷到客戶端,并將請求的關(guān)閉設置為真。 將數(shù)據(jù)刷到客戶端的程序調(diào)用的是fcgi_flush函數(shù)。在此函數(shù)中,關(guān)鍵是在于答應頭的構(gòu)造和寫操作。 程序的寫操作是調(diào)用的safe_write函數(shù),而safe_write函數(shù)中對于最終的寫操作針對win和linux環(huán)境做了區(qū)分, 在Win32下,如果是TCP連接則用send函數(shù),如果是非TCP則和非win環(huán)境一樣使用write函數(shù)。如下代碼:

    #ifdef _WIN32 if (!req->tcp) { ret = write(req->fd, ((char*)buf)+n, count-n); } else { ret = send(req->fd, ((char*)buf)+n, count-n, 0); if (ret <= 0) { errno = WSAGetLastError(); } } #else ret = write(req->fd, ((char*)buf)+n, count-n); #endif

    在發(fā)送了請求的應答后,服務器端將會執(zhí)行關(guān)閉操作,僅限于CGI本身的關(guān)閉,程序執(zhí)行的是fcgi_close函數(shù)。 fcgi_close函數(shù)在前面提的fcgi_finish_request函數(shù)中,在請求應答完后執(zhí)行。同樣,對于win平臺和非win平臺有不同的處理。 其中對于非win平臺調(diào)用的是write函數(shù)。

    以上是一個TCP服務器端實現(xiàn)的簡單說明。這只是我們PHP的CGI模式的基礎(chǔ),在這個基礎(chǔ)上PHP增加了更多的功能。?

    php-fpm

    FastCGI接口方式在腳本解析服務器上啟動一個或者多個守護進程對動態(tài)腳本進行解析,這些進程就是FastCGI進程管理器,或者稱之為FastCGI引擎, spawn-fcgi與PHP-FPM就是支持PHP的兩個FastCGI進程管理器。

    FPM(FastCGI 進程管理器)用于替換 PHP FastCGI 的大部分附加功能,對于高負載網(wǎng)站是非常有用的。

    它的功能包括:

    • 支持平滑停止/啟動的高級進程管理功能;

    • 可以工作于不同的 uid/gid/chroot 環(huán)境下,并監(jiān)聽不同的端口和使用不同的 php.ini 配置文件(可取代 safe_mode 的設置);

    • stdout 和 stderr 日志記錄;

    • 在發(fā)生意外情況的時候能夠重新啟動并緩存被破壞的 opcode;

    • 文件上傳優(yōu)化支持;

    • "慢日志" - 記錄腳本(不僅記錄文件名,還記錄 PHP backtrace 信息,可以使用 ptrace或者類似工具讀取和分析遠程進程的運行數(shù)據(jù))運行所導致的異常緩慢;

    • fastcgi_finish_request()?- 特殊功能:用于在請求完成和刷新數(shù)據(jù)后,繼續(xù)在后臺執(zhí)行耗時的工作(錄入視頻轉(zhuǎn)換、統(tǒng)計處理等);

    • 動態(tài)/靜態(tài)子進程產(chǎn)生;

    • 基本 SAPI 運行狀態(tài)信息(類似Apache的 mod_status);

    • 基于 php.ini 的配置文件。

    使用PHP-FPM來控制PHP-CGI的FastCGI進程

    什么是PHP-CGI

      PHP-CGI是PHP自帶的FastCGI管理器。

      啟動PHP-CGI,使用如下命令:

    php-cgi -b 127.0.0.1:9000

      PHP-CGI的不足

      1、php-cgi變更php.ini配置后需重啟php-cgi才能讓新的php-ini生效,不可以平滑重啟

      2、直接殺死php-cgi進程,php就不能運行了。(PHP-FPM和Spawn-FCGI就沒有這個問題,守護進程會平滑從新生成新的子進程。)

      什么是PHP-FPM

      PHP-FPM是一個PHP FastCGI管理器,是只用于PHP的,可以在 http://php-fpm.org/download下載得到.

      PHP-FPM其實是PHP源代碼的一個補丁,旨在將FastCGI進程管理整合進PHP包中。必須將它patch到你的PHP源代碼中,在編譯安裝PHP后才可以使用。

      現(xiàn)在我們可以在最新的PHP 5.3.2的源碼樹里下載得到直接整合了PHP-FPM的分支,據(jù)說下個版本會融合進PHP的主分支去。相對Spawn-FCGI,PHP-FPM在CPU和內(nèi)存方面的控制都更勝一籌,而且前者很容易崩潰,必須用crontab進行監(jiān)控,而PHP-FPM則沒有這種煩惱。

      PHP5.3.3已經(jīng)集成php-fpm了,不再是第三方的包了。PHP-FPM提供了更好的PHP進程管理方式,可以有效控制內(nèi)存和進程、可以平滑重載PHP配置,比spawn-fcgi具有更多有點,所以被PHP官方收錄了。在./configure的時候帶 –enable-fpm參數(shù)即可開啟PHP-FPM。

      使用PHP-FPM來控制PHP-CGI的FastCGI進程

    /usr/local/php/sbin/php-fpm{start|stop|quit|restart|reload|logrotate}--start 啟動php的fastcgi進程 --stop 強制終止php的fastcgi進程 --quit 平滑終止php的fastcgi進程 --restart 重啟php的fastcgi進程 --reload 重新平滑加載php的php.ini --logrotate 重新啟用log文件

    轉(zhuǎn)載于:https://www.cnblogs.com/abbiebear/p/9360287.html

    總結(jié)

    以上是生活随笔為你收集整理的CGI,FastCGI,PHP-CGI,PHP-FPM的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。