NGINX源碼分析——概覽

1、概況php

    Nginx能夠開啓多個進程,每一個進程擁有最大上限128個子線程以及必定的可用鏈接數。最大客戶端鏈接數等於進程數與鏈接數的乘積,鏈接是在主進程中初始化的,一開始全部鏈接處於空閒狀態。每個客戶端請求進來之後會經過事件處理機制,在LinuxEpoll,在FreeBSD下是KQueue放到空閒的鏈接裏。html

若是設置了線程數,那麼被填充的鏈接會在子線程中處理,不然會在主線程中依次處理。nginx

若是解析出是動態腳本請求,會根據fast-cgi的設置訪問php-cgi進程,php進程數量的多少依據php-fpm.confmax_children的設置。數組

所以Nginx的動態請求能力不只僅依靠Nginx自己的設置,還要調試php-fpm網絡

從源代碼級別上看nginx由如下幾個元素組成:socket

1. worker(進程)函數

2. thread(線程)php-fpm

3. connection(鏈接)post

4. event(事件)ui

5. module(模塊)

6. pool(內存池)

7. cycle(全局設置)

8. log(日誌)

2、MAIN函數

    整個程序從main()開始算,代碼更詳細的內容,能夠查看兩外一篇文章:http://www.cnblogs.com/liqiu/p/3500357.html 

   ngx_max_module = 0;

    for (i = 0; ngx_modules[i]; i++) {

        ngx_modules[i]->index = ngx_max_module++;

    }

    這幾句比較關鍵,對加載的模塊點一下數,看有多少個。ngx_modules並非在原代碼中被賦值的,你先執行一下./configure命令生成用於編譯的make環境。在根目錄會多出來一個文件夾objs,找到ngx_modules.c文件,默認狀況下nginx會加載大約40個模塊,的確很多,若是你不須要那個模塊儘可能仍是去掉好一些。

    接下來比較重要的函數是 ngx_init_cycle(),這個函數初始化系統的配置以及網絡鏈接等,若是是多進程方式加載的會繼續調用ngx_master_process_cycle(),這是main函數中調用的最關鍵的兩個函數。

3、模塊初始化

    ngx_init_cycle()其實是個複雜的初始化函數,首先是加載各子模塊的配置信息、並初始化各組成模塊。任何模塊都有兩個重要接口組成,一個是create_conf,一個是init_conf。分別是建立配置和初始化配置信息。模塊按照前後順序依次初始化,大概是這樣的: 

    &ngx_core_module,

    &ngx_errlog_module,

    &ngx_conf_module,

    &ngx_events_module,

    &ngx_event_core_module,

    &ngx_epoll_module,

    &ngx_http_module,

    &ngx_http_core_module,

    &ngx_http_log_module,

    首先是內核模塊、錯誤日誌、配置模塊、事件模塊、時間內核模塊、EPOLL模塊、http模塊、http內核模塊、http日誌模塊,剩下的模塊都算不上關鍵。 

    epoll是比較關鍵的核心模塊之一,nginx兼容多種IO控制模型,memecached用的是libevent不如nginx徹頭徹尾是本身實現的。

4、socket初始化

    在ngx_init_cycle()中對模塊初始化完畢後,調用ngx_open_listening_sockets()函數對socket進行了初始化。listen80端口之後,調用模塊的另一個重要接口init_module對各模塊進行初始化。並非每一個模塊都對init_module接口進行了定義,在比較重要的模塊中僅有 ngx_http_log_module 對這個接口進行了定義。

5、進程初始化 

    ngx_init_cycle()返回後,主要的工做都是在ngx_master_process_cycle()函數中繼續進行的。ngx_master_process_cycle()函數中的重要過程是調用ngx_start_worker_processes()生成多個子進程,通常nginx是多進程的。ngx_start_worker_processes()函數內部調用ngx_worker_process_cycle()函數創建每一個進程的實際工做內容,在這個函數中首先調用ngx_create_thread()初始化各線程。咱們知道每一個線程都有一個啓動處理函數,nginx的線程處理函數爲ngx_worker_thread_cycle(),內部過程當中最重要的是對ngx_event_thread_process_posted()函數的調用,用於實際處理每一次請求。

    在初始化線程結束後,首先調用ngx_process_events_and_timers()函數,該函數繼續調用ngx_process_events接口監聽事件,通常狀況下對應的函數是ngx_epoll_process_events(),若是使用的是其它種類的IO模型,則應該實現相應的實際函數。這個接口負責把事件投遞到ngx_posted_events事件隊列裏,並在 ngx_event_thread_process_posted()函數中進行處理。 

6、connectionevent

    nginxconnectionevent是按照鏈表的方式進行存放的,區別在於connection是單向鏈表而event是雙向鏈表。nginx中的connection鏈表是提早分配的,定義在ngx_event_process_init()函數內,具體代碼以下: 

    ...

    cycle->connections = ngx_alloc(sizeof(ngx_connection_t) * ecf->connections, cycle->log);

    ...

    i = cycle->connection_n;

    next = NULL;

    do {

        i--;

        c[i].data = next;

        c[i].read = &cycle->read_events[i];

        c[i].write = &cycle->write_events[i];

        c[i].fd = (ngx_socket_t) -1;

        next = &c[i];

#if (NGX_THREADS)

        c[i].lock = 0;

#endif

    } while (i);

    cycle->free_connections = next;

    cycle->free_connection_n = ecf->connections;

    在內存池裏爲全部connection分配空間,並依次初始化,並依次初始化各鏈接的鏈表關係,也就是在data上存下一個connection的指針。在具體應用中經過ngx_get_connection()函數取出空閒的connection並使用。

    至於event是一個雙向鏈表,鏈表的入隊和出隊有下面的定義:

#define ngx_locked_post_event(ev, queue)                                      \if (ev->prev == NULL) {                                                   \

        ev->next = (ngx_event_t *) *queue;                                    \

        ev->prev = (ngx_event_t **) queue;                                    \

        *queue = ev;                                                          \

        if (ev->next) {                                                       \

            ev->next->prev = &ev->next;                                       \

        }                                                                     \

        ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0, "post event %p", ev); \

    } else {                                                                 \

        ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0, "update posted event %p", ev);                         \

    }

#define ngx_post_event(ev, queue)                                             \

    ngx_mutex_lock(ngx_posted_events_mutex);                                  \

    ngx_locked_post_event(ev, queue);                                         \

    ngx_mutex_unlock(ngx_posted_events_mutex);


#define ngx_delete_posted_event(ev)                                           \*(ev->prev) = ev->next;                                                   \

    if (ev->next) {                                                           \

        ev->next->prev = ev->prev;                                            \

    }                                                                         \

    ev->prev = NULL;                                                          \

    ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0, "delete posted event %p", ev);

    簡單說 ngx_post_event 用於插入事件、ngx_delete_posted_event 用於刪除事件。這兩個是宏定義,會比函數定義用起來節省。整個程序就圍繞這eventconnection進行。不斷的投入、拿出。

7、nginx的模塊處理

     nginx由若干模塊組成,但全部模塊所有采用靜態編譯的辦法。咱們以rewrite模塊爲例,咱們輸入./configure --help 命令能夠查看的有關rewrite模塊的描述 --without-http_rewrite_module      disable ngx_http_rewrite_module。這是在auto/options下定義的,在auto/soruces裏面指定了與這個模塊有關的源代碼位置

HTTP_REWRITE_MODULE=ngx_http_rewrite_module

HTTP_REWRITE_SRCS=src/http/modules/ngx_http_rewrite_module.c

    在auto/summary下提供了關於rewrite模塊的依賴條件檢查: 

if [ $HTTP_REWRITE = YES ]; then

    if [ $USE_PCRE = DISABLED ]; then 

cat << END

$0: error: the HTTP rewrite module requires the PCRE library.

You can either disable the module by using --without-http_rewrite_module

option or you have to enable the PCRE support.

END

        exit 1

    fi

    if [ $PCRE = NONE -o $PCRE = NO ]; then

cat << END

$0: error: the HTTP rewrite module requires the PCRE library.

You can either disable the module by using --without-http_rewrite_module

option, or install the PCRE library into the system, or build the PCRE library

statically from the source with nginx by using --with-pcre=<path> option.

END

        exit 1

    fi

fi

    下面是對rewrite模塊代碼分析,其它模塊也帶有一樣的結構。

    通常模塊都會有一個ngx_command_t結構,存放在ngingx配置文件中,這個模塊的一些語法命令,詳細結構爲:

struct ngx_command_s {

    ngx_str_t             name;

    ngx_uint_t            type;

    char               *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

    ngx_uint_t            conf;

    ngx_uint_t            offset;

    void                 *post;

};
  • name 是名稱
  • type 是類型
  • set 是處理這個命令的具體函數
  • conf 屬於那種配置,一共三種 main結構、server結構、location結構。
  • offset 在配置文件中的偏移量。
  • post 沒見到用到。 

    下面是和rewrite模塊中的代碼: 

static ngx_command_t ngx_http_rewrite_commands[] = {

 

    { ngx_string("rewrite"),

      NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF

                       |NGX_CONF_TAKE23,

      ngx_http_rewrite,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

 

    { ngx_string("return"),

      NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_TAKE1,

      ngx_http_rewrite_return,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL }, 

    { ngx_string("break"),

      NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_NOARGS,

      ngx_http_rewrite_break,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL }, 

    { ngx_string("if"),

      NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_BLOCK|NGX_CONF_1MORE,

      ngx_http_rewrite_if,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL },

 

    { ngx_string("set"),

      NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_TAKE2,

      ngx_http_rewrite_set,

      NGX_HTTP_LOC_CONF_OFFSET,

      0,

      NULL }, 

    { ngx_string("rewrite_log"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF |NGX_HTTP_LIF_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_rewrite_loc_conf_t, log),

      NULL },

 

    { ngx_string("uninitialized_variable_warn"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_rewrite_loc_conf_t, uninitialized_variable_warn),

      NULL },

      ngx_null_command

};

    rewrite模塊內部一共有四類函數,關鍵的一類爲一類是動做處理函數,一共有: 

  • ngx_http_rewrite_init 模塊初始化函數。
  • ngx_http_rewrite_handler 在具體請求中的處理函數。 

    這兩個函數經過在模塊初始化的過程當中,把handler推送到phases結構中,這是一個簡單數組,請求會對數組中存放的每個handler依次進行處理。若是咱們本身寫什麼模塊也應該放進去,例如一樣的ngx_http_access_init()等初始化函數也是把本身模塊的ngx_http_access_handler註冊進去。 

    nginx在內部已經定義了全部能夠在http請求中被處理的模塊列表: 

typedef enum {

    NGX_HTTP_POST_READ_PHASE = 0,

    NGX_HTTP_SERVER_REWRITE_PHASE,

    NGX_HTTP_FIND_CONFIG_PHASE,

    NGX_HTTP_REWRITE_PHASE,

    NGX_HTTP_POST_REWRITE_PHASE,

    NGX_HTTP_PREACCESS_PHASE,

    NGX_HTTP_ACCESS_PHASE,

    NGX_HTTP_POST_ACCESS_PHASE,

    NGX_HTTP_CONTENT_PHASE, 

    NGX_HTTP_LOG_PHASE

} ngx_http_phases;

     和rewrite有關的一共是兩個,都是在ngx_http_rewrite_handler()函數中進行處理,一類爲與配置文件有關的建立和合並函數,一共有: 

  • ngx_http_rewrite_create_loc_conf
  • ngx_http_rewrite_merge_loc_conf 

    一類做爲common命令的解析函數,一共有:

  • ngx_http_rewrite
  • ngx_http_rewrite_return
  • ngx_http_rewrite_break
  • ngx_http_rewrite_if
  • ngx_http_rewrite_set 

    一類爲解析命令中內部使用的函數,一共有:

  • ngx_http_rewrite_if_condition
  • ngx_http_rewrite_variable
  • ngx_http_rewrite_value
  • ngx_http_rewrite_var
相關文章
相關標籤/搜索