一.PHP基本架構(gòu)

原文github地址

1.PHP概述

1.1 PHP的歷史發(fā)展

1995年由Lerdorf創(chuàng)建PHP,高級(jí)腳本語言,尤其適合Web開發(fā),快速、靈活和實(shí)用是PHP最重要的特點(diǎn)。

在1997年,任職于 Technion IIT 公司的兩個(gè)以色列程序設(shè)計(jì)師:Zeev Suraski 和 Andi Gutmans,重寫了PHP的解析器。

1998年6月正式發(fā)布 PHP 3。Zeev Suraski 和 Andi Gutmans 在 PHP 3 發(fā)布后開始改寫 PHP 的核心,這個(gè)在1999年發(fā)布的解析器稱為 Zend Engine。

在2000年5月22日,以Zend Engine 1.0為基礎(chǔ)的PHP 4正式發(fā)布。

2004年7月13日則發(fā)布了PHP 5,PHP 5則使用了第二代的Zend Engine。

2015年6月11日,PHP官網(wǎng)發(fā)布消息,正式公開發(fā)布PHP7第一版的alpha版本。

1.2 特性

PHP 獨(dú)特的語法混合了 C、Java、Perl 以及 PHP 自創(chuàng)新的語法,豐富的語法支持、同時(shí)支持面向?qū)ο蟆⒚嫦蜻^程,相比C、Java等語言具有語法簡潔、使用靈活、開發(fā)效率高。

1.3 PHP的相關(guān)組成
1.3.1 SAPI

SAPI是PHP的接入層,它接收用戶的請(qǐng)求,然后調(diào)用PHP內(nèi)核提供的一些接口完成PHP腳本的執(zhí)行,所以嚴(yán)格意義上講SAPI并不算PHP內(nèi)核的一部分。PHP本身可以理解為是一個(gè)庫函數(shù),提供語言的編譯與執(zhí)行服務(wù),它有標(biāo)準(zhǔn)的輸入、輸出。

PHP中常用的SAPI有CLI、php-fpm(CGI),CLI是命令行下執(zhí)行PHP腳本的實(shí)現(xiàn):bin/php script.php,它是單進(jìn)程的,處理模型比較簡單,而php-fpm相對(duì)比較復(fù)雜,它實(shí)現(xiàn)了網(wǎng)絡(luò)處理模塊,用于與web服務(wù)器交互。

1.3.2 Zend引擎

Zend是PHP語言實(shí)現(xiàn)的最為重要的部分,是PHP最基礎(chǔ)、最核心的部分,它的源碼在/Zend目錄下,PHP代碼從編譯到執(zhí)行都是由Zend完成的,后面章節(jié)絕大部分的源碼分析都是針對(duì)Zend的。Zend整體由兩個(gè)部分組成:

  • 編譯器: 負(fù)責(zé)將PHP代碼編譯為抽象語法樹,然后進(jìn)一步編譯為可執(zhí)行的opcodes,這個(gè)過程相當(dāng)于GCC的工作,編譯器是一個(gè)語言實(shí)現(xiàn)的基礎(chǔ)
  • 執(zhí)行器: 負(fù)責(zé)執(zhí)行編譯器輸出的opcodes,也就是執(zhí)行PHP腳本中編寫的代碼邏輯

2. 執(zhí)行流程

PHP的生命周期
image.png
  • php_module_startup() 模塊初始化階段
  • php_request_startup() 請(qǐng)求初始化階段
  • php_execute_script() 執(zhí)行PHP腳本階段
  • php_request_shutdown() 請(qǐng)求結(jié)束階段
  • php_module_shutdown() 模塊關(guān)閉階段

3. FPM

3.1 概述

FPM(FastCGI Process Manager)是PHP FastCGI運(yùn)行模式的一個(gè)進(jìn)程管理器,核心功能是進(jìn)程管理,那么它用來管理什么進(jìn)程呢?這個(gè)問題就需要從FastCGI說起了。

FastCGI是Web服務(wù)器(如:Nginx、Apache)和處理程序之間的一種通信協(xié)議,它是與Http類似的一種應(yīng)用層通信協(xié)議,注意:它只是一種協(xié)議!

web服務(wù)器來處理http請(qǐng)求,然后將解析的結(jié)果再通過FastCGI協(xié)議轉(zhuǎn)發(fā)給處理程序,處理程序處理完成后將結(jié)果返回給web服務(wù)器,web服務(wù)器再返回給用戶,如下圖所示。

image.png

PHP實(shí)現(xiàn)了FastCGI協(xié)議的解析,但是并沒有具體實(shí)現(xiàn)網(wǎng)絡(luò)處理,一般的處理模型:多進(jìn)程、多線程。多進(jìn)程模型通常是主進(jìn)程只負(fù)責(zé)管理子進(jìn)程,而基本的網(wǎng)絡(luò)事件由各個(gè)子進(jìn)程處理,nginx、fpm就是這種模式。

3.2 基本實(shí)現(xiàn)

fpm的實(shí)現(xiàn)就是創(chuàng)建一個(gè)master進(jìn)程,在master進(jìn)程中創(chuàng)建并監(jiān)聽socket,然后fork出多個(gè)子進(jìn)程,這些子進(jìn)程各自accept請(qǐng)求。

fpm的子進(jìn)程子進(jìn)程在啟動(dòng)后阻塞在accept上,同時(shí)只能響應(yīng)一個(gè)請(qǐng)求,只有把這個(gè)請(qǐng)求處理完成后才會(huì)accept下一個(gè)請(qǐng)求。(nginx則是一個(gè)進(jìn)程會(huì)同時(shí)連接多個(gè)請(qǐng)求,它是非阻塞的模型,只處理活躍的套接字)。

fpm的master進(jìn)程與worker進(jìn)程之間不會(huì)直接進(jìn)行通信,master通過共享內(nèi)存獲取worker進(jìn)程的信息,比如worker進(jìn)程當(dāng)前狀態(tài)、已處理請(qǐng)求數(shù)等,當(dāng)master進(jìn)程要?dú)⒌粢粋€(gè)worker進(jìn)程時(shí)則通過發(fā)送信號(hào)的方式通知worker進(jìn)程。

fpm可以同時(shí)監(jiān)聽多個(gè)端口,每個(gè)端口對(duì)應(yīng)一個(gè)worker pool,而每個(gè)pool下對(duì)應(yīng)多個(gè)worker進(jìn)程,類似nginx中server概念。

image.png

具體實(shí)現(xiàn)上worker pool通過fpm_worker_pool_s這個(gè)結(jié)構(gòu)表示,多個(gè)worker pool組成一個(gè)單鏈表:

struct fpm_worker_pool_s {
    struct fpm_worker_pool_s *next; //指向下一個(gè)worker pool
    struct fpm_worker_pool_config_s *config; //conf配置:pm、max_children、start_servers...
    int listening_socket; //監(jiān)聽的套接字
    ...

    //以下這個(gè)值用于master定時(shí)檢查、記錄worker數(shù)
    struct fpm_child_s *children; //當(dāng)前pool的worker鏈表
    int running_children; //當(dāng)前pool的worker運(yùn)行總數(shù)
    int idle_spawn_rate;
    int warn_max_children;

    struct fpm_scoreboard_s *scoreboard; //記錄worker的運(yùn)行信息,比如空閑、忙碌worker數(shù)
    ...
}
3.3 FPM的初始化

fpm的啟動(dòng)流程,從main()函數(shù)開始:

//sapi/fpm/fpm/fpm_main.c
int main(int argc, char *argv[])
{
    ...
    //注冊(cè)SAPI:將全局變量sapi_module設(shè)置為cgi_sapi_module
    sapi_startup(&cgi_sapi_module);
    ...
    //執(zhí)行php_module_starup()
    if (cgi_sapi_module.startup(&cgi_sapi_module) == FAILURE) {
        return FPM_EXIT_SOFTWARE;
    }
    ...
    //初始化
    if(0 > fpm_init(...)){
        ...
    }
    ...
    fpm_is_running = 1;

    fcgi_fd = fpm_run(&max_requests);//后面都是worker進(jìn)程的操作,master進(jìn)程不會(huì)走到下面
    parent = 0;
    ...
}

fpm_init()主要有以下幾個(gè)關(guān)鍵操作:

  • 1.fpm_conf_init_main():
    解析php-fpm.conf配置文件,分配worker pool內(nèi)存結(jié)構(gòu)并保存到全局變量中:fpm_worker_all_pools,各worker pool配置解析到fpm_worker_pool_s->config中。
  • 2.fpm_scoreboard_init_main():
    分配用于記錄worker進(jìn)程運(yùn)行信息的共享內(nèi)存,按照worker pool的最大worker進(jìn)程數(shù)分配,每個(gè)worker pool分配一個(gè)fpm_scoreboard_s結(jié)構(gòu),pool下對(duì)應(yīng)的每個(gè)worker進(jìn)程分配一個(gè)fpm_scoreboard_proc_s結(jié)構(gòu),各結(jié)構(gòu)的對(duì)應(yīng)關(guān)系如下圖。
image.png
  • 3.fpm_signals_init_main(): master的信號(hào)處理
static int sp[2];

int fpm_signals_init_main()
{
    struct sigaction act;

    //創(chuàng)建一個(gè)全雙工管道
    if (0 > socketpair(AF_UNIX, SOCK_STREAM, 0, sp)) {
        return -1;
    }
    //注冊(cè)信號(hào)處理handler
    act.sa_handler = sig_handler;
    sigfillset(&act.sa_mask);
    if (0 > sigaction(SIGTERM,  &act, 0) ||
        0 > sigaction(SIGINT,   &act, 0) ||
        0 > sigaction(SIGUSR1,  &act, 0) ||
        0 > sigaction(SIGUSR2,  &act, 0) ||
        0 > sigaction(SIGCHLD,  &act, 0) ||
        0 > sigaction(SIGQUIT,  &act, 0)) {
        return -1;
    }
    return 0;
}
  • 4.fpm_sockets_init_main(): 創(chuàng)建每個(gè)worker pool的socket套接字。
  • 5.fpm_event_init_main():
    啟動(dòng)master的事件管理,fpm實(shí)現(xiàn)了一個(gè)事件管理器用于管理IO、定時(shí)事件,其中IO事件通過kqueue、epoll、poll、select等管理,定時(shí)事件就是定時(shí)器,一定時(shí)間后觸發(fā)某個(gè)事件。
    在fpm_init()初始化完成后接下來就是最關(guān)鍵的fpm_run()操作了,此環(huán)節(jié)將fork子進(jìn)程,啟動(dòng)進(jìn)程管理器,另外master進(jìn)程將不會(huì)再返回,只有各worker進(jìn)程會(huì)返回,也就是說fpm_run()之后的操作均是worker進(jìn)程的。
int fpm_run(int *max_requests)
{
    struct fpm_worker_pool_s *wp;
    for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
        //調(diào)用fpm_children_make() fork子進(jìn)程
        is_parent = fpm_children_create_initial(wp);
        
        if (!is_parent) {
            goto run_child;
        }
    }
    //master進(jìn)程將進(jìn)入event循環(huán),不再往下走
    fpm_event_loop(0);

run_child: //只有worker進(jìn)程會(huì)到這里

    *max_requests = fpm_globals.max_requests;
    return fpm_globals.listening_socket; //返回監(jiān)聽的套接字
}

在fork后worker進(jìn)程返回了監(jiān)聽的套接字繼續(xù)main()后面的處理,而master將永遠(yuǎn)阻塞在fpm_event_loop(),接下來分別介紹master、worker進(jìn)程的后續(xù)操作。

3.4 請(qǐng)求處理

fpm_run()執(zhí)行后將fork出worker進(jìn)程,worker進(jìn)程返回main()中繼續(xù)向下執(zhí)行,后面的流程就是worker進(jìn)程不斷accept請(qǐng)求,然后執(zhí)行PHP腳本并返回。整體流程如下:

  • 1.等待請(qǐng)求: worker進(jìn)程阻塞在fcgi_accept_request()等待請(qǐng)求;
  • 2.解析請(qǐng)求: fastcgi請(qǐng)求到達(dá)后被worker接收,然后開始接收并解析請(qǐng)求數(shù)據(jù),直到request數(shù)據(jù)完全到達(dá);
  • 3.請(qǐng)求初始化: 執(zhí)行php_request_startup(),此階段會(huì)調(diào)用每個(gè)擴(kuò)展的:PHP_RINIT_FUNCTION();
  • 4.編譯、執(zhí)行: 由php_execute_script()完成PHP腳本的編譯、執(zhí)行;
  • 5.關(guān)閉請(qǐng)求: 請(qǐng)求完成后執(zhí)行php_request_shutdown(),此階段會(huì)調(diào)用每個(gè)擴(kuò)展的:PHP_RSHUTDOWN_FUNCTION(),然后進(jìn)入步驟1等待下一個(gè)請(qǐng)求。

worker進(jìn)程一次請(qǐng)求的處理被劃分為5個(gè)階段:

  • FPM_REQUEST_ACCEPTING: 等待請(qǐng)求階段
  • FPM_REQUEST_READING_HEADERS: 讀取fastcgi請(qǐng)求header階段
  • FPM_REQUEST_INFO: 獲取請(qǐng)求信息階段,此階段是將請(qǐng)求的method、query stirng、request uri等信息保存到各worker進(jìn)程的fpm_scoreboard_proc_s結(jié)構(gòu)中,此操作需要加鎖,因?yàn)閙aster進(jìn)程也會(huì)操作此結(jié)構(gòu)
  • FPM_REQUEST_EXECUTING: 執(zhí)行請(qǐng)求階段
  • FPM_REQUEST_END: 沒有使用
  • FPM_REQUEST_FINISHED: 請(qǐng)求處理完成

worker處理到各個(gè)階段時(shí)將會(huì)把當(dāng)前階段更新到fpm_scoreboard_proc_s->request_stage,master進(jìn)程正是通過這個(gè)標(biāo)識(shí)判斷worker進(jìn)程是否空閑的。

3.5 進(jìn)程管理

這一節(jié)我們來看下master是如何管理worker進(jìn)程的,首先介紹下三種不同的進(jìn)程管理方式:

  • static: 這種方式比較簡單,在啟動(dòng)時(shí)master按照pm.max_children配置fork出相應(yīng)數(shù)量的worker進(jìn)程,即worker進(jìn)程數(shù)是固定不變的
  • dynamic: 動(dòng)態(tài)進(jìn)程管理,首先在fpm啟動(dòng)時(shí)按照pm.start_servers初始化一定數(shù)量的worker,運(yùn)行期間如果master發(fā)現(xiàn)空閑worker數(shù)低于pm.min_spare_servers配置數(shù)(表示請(qǐng)求比較多,worker處理不過來了)則會(huì)fork worker進(jìn)程,但總的worker數(shù)不能超過pm.max_children,如果master發(fā)現(xiàn)空閑worker數(shù)超過了pm.max_spare_servers(表示閑著的worker太多了)則會(huì)殺掉一些worker,避免占用過多資源,master通過這4個(gè)值來控制worker數(shù)
  • ondemand: 這種方式一般很少用,在啟動(dòng)時(shí)不分配worker進(jìn)程,等到有請(qǐng)求了后再通知master進(jìn)程fork worker進(jìn)程,總的worker數(shù)不超過pm.max_children,處理完成后worker進(jìn)程不會(huì)立即退出,當(dāng)空閑時(shí)間超過pm.process_idle_timeout后再退出
    前面介紹到在fpm_run()master進(jìn)程將進(jìn)入fpm_event_loop()
void fpm_event_loop(int err)
{
    //創(chuàng)建一個(gè)io read的監(jiān)聽事件,這里監(jiān)聽的就是在fpm_init()階段中通過socketpair()創(chuàng)建管道sp[0]
    //當(dāng)sp[0]可讀時(shí)將回調(diào)fpm_got_signal()
    fpm_event_set(&signal_fd_event, fpm_signals_get_fd(), FPM_EV_READ, &fpm_got_signal, NULL);
    fpm_event_add(&signal_fd_event, 0);

    //如果在php-fpm.conf配置了request_terminate_timeout則啟動(dòng)心跳檢查
    if (fpm_globals.heartbeat > 0) {
        fpm_pctl_heartbeat(NULL, 0, NULL);
    }
    //定時(shí)觸發(fā)進(jìn)程管理
    fpm_pctl_perform_idle_server_maintenance_heartbeat(NULL, 0, NULL);
    
    //進(jìn)入事件循環(huán),master進(jìn)程將阻塞在此
    while (1) {
        ...
        //等待IO事件
        ret = module->wait(fpm_event_queue_fd, timeout);
        ...
        //檢查定時(shí)器事件
        ...
    }
}

這就是master整體的處理,其進(jìn)程管理主要依賴注冊(cè)的幾個(gè)事件,接下來我們?cè)敿?xì)分析下這幾個(gè)事件的功能。

(1)sp[1]管道可讀事件:

fpm_init()階段master曾創(chuàng)建了一個(gè)全雙工的管道:sp,然后在這里創(chuàng)建了一個(gè)sp[0]可讀的事件,當(dāng)sp[0]可讀時(shí)將交由fpm_got_signal()處理,向sp[1]寫數(shù)據(jù)時(shí)sp[0]才會(huì)可讀,那么什么時(shí)機(jī)會(huì)向sp[1]寫數(shù)據(jù)呢?前面已經(jīng)提到了:當(dāng)master收到注冊(cè)的那幾種信號(hào)時(shí)會(huì)寫入sp[1]端,這個(gè)時(shí)候?qū)⒂|發(fā)sp[0]可讀事件。

image.png

這個(gè)事件是master用于處理信號(hào)的,我們根據(jù)master注冊(cè)的信號(hào)逐個(gè)看下不同用途:

  • SIGINT/SIGTERM/SIGQUIT: 退出fpm,在master收到退出信號(hào)后將向所有的worker進(jìn)程發(fā)送退出信號(hào),然后master退出
  • SIGUSR1: 重新加載日志文件,生產(chǎn)環(huán)境中通常會(huì)對(duì)日志進(jìn)行切割,切割后會(huì)生成一個(gè)新的日志文件,如果fpm不重新加載將無法繼續(xù)寫入日志,這個(gè)時(shí)候就需要向master發(fā)送一個(gè)USR1的信號(hào)
  • SIGUSR2: 重啟fpm,首先master也是會(huì)向所有的worker進(jìn)程發(fā)送退出信號(hào),然后master會(huì)調(diào)用execvp()重新啟動(dòng)fpm,最后舊的master退出
  • SIGCHLD: 這個(gè)信號(hào)是子進(jìn)程退出時(shí)操作系統(tǒng)發(fā)送給父進(jìn)程的,子進(jìn)程退出時(shí),內(nèi)核將子進(jìn)程置為僵尸狀態(tài),這個(gè)進(jìn)程稱為僵尸進(jìn)程,它只保留最小的一些內(nèi)核數(shù)據(jù)結(jié)構(gòu),以便父進(jìn)程查詢子進(jìn)程的退出狀態(tài),只有當(dāng)父進(jìn)程調(diào)用wait或者waitpid函數(shù)查詢子進(jìn)程退出狀態(tài)后子進(jìn)程才告終止,fpm中當(dāng)worker進(jìn)程因?yàn)楫惓T?比如coredump了)退出而非master主動(dòng)殺掉時(shí)master將受到此信號(hào),這個(gè)時(shí)候父進(jìn)程將調(diào)用waitpid()查下子進(jìn)程的退出,然后檢查下是不是需要重新fork新的worker

具體處理邏輯在fpm_got_signal()函數(shù)中,這里不再羅列。

(2)fpm_pctl_perform_idle_server_maintenance_heartbeat():

這是進(jìn)程管理實(shí)現(xiàn)的主要事件,master啟動(dòng)了一個(gè)定時(shí)器,每隔1s觸發(fā)一次,主要用于dynamic、ondemand模式下的worker管理,master會(huì)定時(shí)檢查各worker pool的worker進(jìn)程數(shù),通過此定時(shí)器實(shí)現(xiàn)worker數(shù)量的控制,處理邏輯如下:

static void fpm_pctl_perform_idle_server_maintenance(struct timeval *now)
{
    for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
        struct fpm_child_s *last_idle_child = NULL; //空閑時(shí)間最久的worker
        int idle = 0; //空閑worker數(shù)
        int active = 0; //忙碌worker數(shù)
        
        for (child = wp->children; child; child = child->next) {
            //根據(jù)worker進(jìn)程的fpm_scoreboard_proc_s->request_stage判斷
            if (fpm_request_is_idle(child)) {
                //找空閑時(shí)間最久的worker
                ...
                idle++;
            }else{
                active++;
            }
        }
        ...
        //ondemand模式
        if (wp->config->pm == PM_STYLE_ONDEMAND) {
            if (!last_idle_child) continue;

            fpm_request_last_activity(last_idle_child, &last);
            fpm_clock_get(&now);
            if (last.tv_sec < now.tv_sec - wp->config->pm_process_idle_timeout) {
                //如果空閑時(shí)間最長的worker空閑時(shí)間超過了process_idle_timeout則殺掉該worker
                last_idle_child->idle_kill = 1;
                fpm_pctl_kill(last_idle_child->pid, FPM_PCTL_QUIT);
            } 
            continue;
        }
        //dynamic
        if (wp->config->pm != PM_STYLE_DYNAMIC) continue;
        if (idle > wp->config->pm_max_spare_servers && last_idle_child) {
            //空閑worker太多了,殺掉
            last_idle_child->idle_kill = 1;
            fpm_pctl_kill(last_idle_child->pid, FPM_PCTL_QUIT);
            wp->idle_spawn_rate = 1;
            continue;
        }
        if (idle < wp->config->pm_min_spare_servers) {
            //空閑worker太少了,如果總worker數(shù)未達(dá)到max數(shù)則fork
            ...
        }
    }
}

(3)fpm_pctl_heartbeat():

這個(gè)事件是用于限制worker處理單個(gè)請(qǐng)求最大耗時(shí)的,php-fpm.conf中有一個(gè)request_terminate_timeout的配置項(xiàng),如果worker處理一個(gè)請(qǐng)求的總時(shí)長超過了這個(gè)值那么master將會(huì)向此worker進(jìn)程發(fā)送kill -TERM信號(hào)殺掉worker進(jìn)程,此配置單位為秒,默認(rèn)值為0表示關(guān)閉此機(jī)制,另外fpm打印的slow log也是在這里完成的。

static void fpm_pctl_check_request_timeout(struct timeval *now)
{   
    struct fpm_worker_pool_s *wp;

    for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
        int terminate_timeout = wp->config->request_terminate_timeout;
        int slowlog_timeout = wp->config->request_slowlog_timeout;
        struct fpm_child_s *child;

        if (terminate_timeout || slowlog_timeout) { 
            for (child = wp->children; child; child = child->next) {
                //檢查當(dāng)前當(dāng)前worker處理的請(qǐng)求是否超時(shí)
                fpm_request_check_timed_out(child, now, terminate_timeout, slowlog_timeout);
            }
        }
    }
}

除了上面這幾個(gè)事件外還有一個(gè)沒有提到,那就是ondemand模式下master監(jiān)聽的新請(qǐng)求到達(dá)的事件,因?yàn)閛ndemand模式下fpm啟動(dòng)時(shí)是不會(huì)預(yù)創(chuàng)建worker的,有請(qǐng)求時(shí)才會(huì)生成子進(jìn)程,所以請(qǐng)求到達(dá)時(shí)需要通知master進(jìn)程,這個(gè)事件是在fpm_children_create_initial()時(shí)注冊(cè)的,事件處理函數(shù)為fpm_pctl_on_socket_accept(),具體邏輯這里不再展開,比較容易理解。

到目前為止我們已經(jīng)把fpm的核心實(shí)現(xiàn)介紹完了,事實(shí)上fpm的實(shí)現(xiàn)還是比較簡單的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 第一章 Nginx簡介 Nginx是什么 沒有聽過Nginx?那么一定聽過它的“同行”Apache吧!Ngi...
    JokerW閱讀 33,040評(píng)論 24 1,002
  • Nginx的工作原理 1.Nginx的模塊與工作原理 Nginx由內(nèi)核和模塊組成,其中,內(nèi)核的設(shè)計(jì)非常微小和簡潔,...
    架構(gòu)飛毛腿閱讀 6,333評(píng)論 1 27
  • php7基礎(chǔ)邏輯 worker進(jìn)程 進(jìn)程管理方式 static:啟動(dòng)master的時(shí)候按照pm.max_child...
    影帆閱讀 692評(píng)論 0 1
  • 是誰踩碎了一地金色 任由將枯的葉跳舞 是誰碾壓了一簇潔白 任由將開的花哭泣 你在腳踏車上臆想 不經(jīng)意間讓悲傷流露 ...
    惘然一書閱讀 298評(píng)論 0 1
  • 四月第一篇日記,簡單打卡。 暴風(fēng)雨之前的平靜,我總是很平靜。
    發(fā)呆的李相赫閱讀 132評(píng)論 0 0

友情鏈接更多精彩內(nèi)容