PHP-FPM及其三種運行方式

PHP-FPM及其三種運行方式
php-fpm和FastCGI是什麼?
fpm的基本實現
worker工作流程
fpm啓動流程部分源碼
php-fpm運行的三種模式
`static`模式:靜態模式
`ondemand` 模式:按需分配模式
`dynamic`模式:動態模式
總結
php-fpm和FastCGI是什麼?
PHP-FPM(FastCGI Process Manager)是一個PHPFastCGI進程管理器,從其英文名稱和定義可以看出,FPM的核心功能就是進程管理。

FastCGI可以理解爲一種協議,用於web服務器(nginx、Apache)和處理程序間進行通信,是一種應用層通信協議。

工作原理大致如下圖


fpm的基本實現
簡單來說,fpm的實現就是創建一個master進程,在master進程中創建worker pool並監聽socket,然後fork出多個子進程(work),這些worker在啓動後阻塞在fcgi_accept_request()上,各自accept請求,有請求到達後worker開始讀取請求數據,讀取完成後開始處理然後再返回,在這期間是不會接收其它請求的,也就是說fpm的子進程同時只能響應一個請求,只有把這個請求處理完成後纔會accept下一個請求。
fpm的master進程與worker進程之間不會直接進行通信,master通過共享內存獲取worker進程的信息,比如worker進程當前狀態、已處理請求數等,當master進程要殺掉一個worker進程時則通過發送信號的方式通知worker進程。
fpm可以同時監聽多個端口,每個端口對應一個worker pool,而每個pool下對應多個worker進程,類似nginx中server概念, 在php-fpm.conf中可以配置多個,例如:
[web1]
listen:127.0.0.1:9000
[web2]
listen:127.0.0.1:9001
大致原理如下圖

php-fpm源碼結構
struct fpm_worker_pool_s {
    struct fpm_worker_pool_s *next; //指向下一個worker pool
    struct fpm_worker_pool_config_s *config; //conf配置:pm、max_children、start_servers...
     ...
    struct fpm_child_s *children; //當前pool的worker鏈表
    int running_children; //已啓動的進程數
    int idle_spawn_rate;//空閒率
    int warn_max_children;//最大work值
    struct fpm_scoreboard_s *scoreboard; //記錄worker的運行信息,比如空閒、忙碌worker數
    ...
}

1
2
3
4
5
6
7
8
9
10
11
12
以上代碼片段只保留了部分本文所需要的內容

worker工作流程
worker的工作流程包含以下幾個步驟

等待請求:fcgi_accept_request()阻塞等待請求
接收請求:fastcgi請求到達後被worker接收並解析,一直到完全接收,然後將method、query、uri等信息保存到worker進程的fpm_scoreboard_proc_s結構中
初始化請求:php_request_startup()執行,此步驟會調用每個擴展的PHP_RINIT_FUNCTION方法,初始化一些操作
處理請求(編譯、執行):php代碼編譯執行階段,由 php_execute_script方法完成
關閉請求:返回響應,執行php_request_shutdown方法關閉請求,然後進入第一步繼續等待請求,此步驟會執行每個擴展的PHP_RSHUTDOWN_FUNCTION進行一些收尾工作
fpm啓動流程部分源碼
int main(int argc, char *argv[])
{
    ...變量定義,參數初始化
    //註冊SAPI
    sapi_startup(&cgi_sapi_module);
    ...
    //執行php_module_starup()
    if (cgi_sapi_module.startup(&cgi_sapi_module) == FAILURE) {
        return FPM_EXIT_SOFTWARE;
    }
    //初始化
    if(0 > fpm_init(...)){
        //記錄日誌並退出
        return FPM_EXIT_CONFIG;
    }
    ...
    fpm_is_running = 1;//fpm運行狀態標識
    fcgi_fd = fpm_run(&max_requests);//進程初始化,調用fork()創建work進程
     ...
    fcgi_init_request(&request, fcgi_fd); //初始化請求;
    //此階段的php_request_startup()會調用每個擴展的:PHP_RINIT_FUNCTION();
    if (UNEXPECTED(php_request_startup() == FAILURE)) {
    ...
    }
    ...
    php_fopen_primary_script(&file_handle TSRMLS_CC); //打開腳本; 
    ...
    php_execute_script(&file_handle TSRMLS_CC); //執行腳本; 
    ...
    //worker進程退出
    php_module_shutdown();
    ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
以上代碼片段只保留了部分本文所需要的內容

php-fpm運行的三種模式
static模式:靜態模式
該模式比較簡單,在啓動時按照配置pm.max_children啓動固定數量的的進程,這些進程阻塞進行請求的接收
方法執行流程:
fpm_run()->fpm_children_create_initial()->fpm_children_make()
啓動fpm的時候會調用fpm_run方法,而fpm_run方法內部會調用子進程初始化方法fpm_children_create_initial,
在該方法內部會調用fpm_children_make方法創建worker進程。

ondemand 模式:按需分配模式
ondemand模式,運行流程和第一步相同,不同之處是在第二個函數中不會分配work進程,而是註冊了一個事件回調函數fpm_pctl_on_socket_accept(),部分代碼如下:
if (wp->config->pm == PM_STYLE_ONDEMAND) { 
     wp->ondemand_event = (struct fpm_event_s *)malloc(sizeof(struct fpm_event_s));   
      ......   
     memset(wp->ondemand_event, 0, sizeof(struct fpm_event_s));    
      fpm_event_set(wp->ondemand_event,wp->listening_socket,FPM_EV_READ|FPM_EV_EDGE,fpm_pctl_on_socket_accept, wp);    
      ......
}
1
2
3
4
5
6
7
以上代碼片段只保留了部分本文所需要的內容

ondemand模式work進程的創建,回調函數fpm_pctl_on_socket_accept()的部分代碼如下:
if (wp->running_children >= wp->config->pm_max_children) {   //判斷進程數是否超過最大限制
       ......    
       return;
}
for (child = wp->children; child; child = child->next) { 
   //fpm_request_is_idle函數返回return proc->request_stage == FPM_REQUEST_ACCEPTING
   if (fpm_request_is_idle(child)) { 
       return;   // FPM_REQUEST_ACCEPTING代表處於等待請求階段
 }
}
...... 
fpm_children_make(wp, 1, 1, 1);//創建work進程
1
2
3
4
5
6
7
8
9
10
11
12
以上代碼片段只保留了部分本文所需要的內容

ondemand模式work進程的關閉
PFM註冊了一個定時事件fpm_pctl_perform_idle_server_maintenance_heartbeat檢查當前模式下work進程的運行情況,當空閒進程等待請求時間超過pm_process_idle_timeout後,會對最後一個空閒worker進程發出關閉信號,此操作由主進程進行處理,部分代碼如下:
if (wp->config->pm == PM_STYLE_ONDEMAND) {
    struct timeval last, now;    
    if (!last_idle_child) continue;//最後一個idle進程
     ......
    // last.tv_sec爲上次接收請求的時間
    if (last.tv_sec < now.tv_sec - wp->config->pm_process_idle_timeout) {
        last_idle_child->idle_kill = 1;
        fpm_pctl_kill(last_idle_child->pid, FPM_PCTL_QUIT);
    }
    continue;
}
1
2
3
4
5
6
7
8
9
10
11
以上代碼片段只保留了部分本文所需要的內容

dynamic模式:動態模式
dynamic模式,啓動時分配固定數量的work進程,然後隨着請求的增加會增加進程數,此模式下幾個重要的配置項如下:
max_children 最大進程數
pm_max_spare_servers 允許最大的空閒進程數
min_spare_servers 允許最小的空閒進程數
start_servers 啓動時的進程數
執行過程和ondemand模式類似,啓動時主進程都會創建一個定時事件來定時檢查work的運行狀況,不同的是dynamic模式初始化的時候會創建一定數量的進程,而ondemand模式不會創建,部分代碼如下:

if (idle > wp->config->pm_max_spare_servers && last_idle_child) {//空閒進程數大於配置的允許空閒最大進程數,則關閉進程
    last_idle_child->idle_kill = 1;
    fpm_pctl_kill(last_idle_child->pid, FPM_PCTL_QUIT);
    ......
}
if (idle < wp->config->pm_min_spare_servers) {//空閒進程數小於配置允許的最小進程數,則創建進程
    if (wp->running_children >= wp->config->pm_max_children) {//如果達到最大上限,則不再創建
        ......
        continue;
    }
     ......
//此處計算出需要擴充的進程數,從wp->idle_spawn_rate, wp->config->pm_min_spare_servers – idle, wp->config->pm_max_children - wp->running_children三個中選出最小的一個作爲本次要擴充的進程數進行擴充
if (wp->idle_spawn_rate >= 8) {
        zlog(ZLOG_WARNING, "[pool %s] seems busy (you may need to increase pm.start_servers, or pm.min/max_spare_servers), spawning %d children, there are %d idle, and %d total children", wp->config->name, wp->idle_spawn_rate, idle, wp->running_children);
    }
    if (wp->idle_spawn_rate < FPM_MAX_SPAWN_RATE) {
        wp->idle_spawn_rate *= 2;//當前進程分配基數小於配置值時候,會以2的倍數進行增長
    }
    ......
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
總結
以上就是本人對php-fpm以及php-fpm的三種運行方式的理解,三種運行方式中,
static模式最簡單,但是靈活性不夠高
ondemand模式相對static模式比較複雜,會根據請求量的增加動態增加,但是處理完請求後不會立即釋放,而是由定時事件定時的檢測空閒到一定時間的進程纔會釋放
dynamic模式類似於ondemand模式,但進程的回收機制不同於ondemand模式,會根據idle數量進行增加和減少worker數量

三種運行方式各有自己的優勢,用哪種方式更合適,要根據自己的業務場景,選擇合適的運行方式
--------------------- 
作者:對你很有feel 
來源:CSDN 
原文:https://blog.csdn.net/njrclj/article/details/85062459 
版權聲明:本文爲博主原創文章,轉載請附上博文鏈接!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章