1、概況php
Nginx能夠開啓多個進程,每一個進程擁有最大上限128個子線程以及必定的可用鏈接數。最大客戶端鏈接數等於進程數與鏈接數的乘積,鏈接是在主進程中初始化的,一開始全部鏈接處於空閒狀態。每個客戶端請求進來之後會經過事件處理機制,在Linux是Epoll,在FreeBSD下是KQueue放到空閒的鏈接裏。html
若是設置了線程數,那麼被填充的鏈接會在子線程中處理,不然會在主線程中依次處理。nginx
若是解析出是動態腳本請求,會根據fast-cgi的設置訪問php-cgi進程,php進程數量的多少依據php-fpm.conf中max_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(日誌)
整個程序從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函數中調用的最關鍵的兩個函數。
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徹頭徹尾是本身實現的。
在ngx_init_cycle()中對模塊初始化完畢後,調用ngx_open_listening_sockets()函數對socket進行了初始化。在listen上80端口之後,調用模塊的另一個重要接口init_module對各模塊進行初始化。並非每一個模塊都對init_module接口進行了定義,在比較重要的模塊中僅有 ngx_http_log_module 對這個接口進行了定義。
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()函數中進行處理。
nginx的connection和event是按照鏈表的方式進行存放的,區別在於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 用於刪除事件。這兩個是宏定義,會比函數定義用起來節省。整個程序就圍繞這event和connection進行。不斷的投入、拿出。
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; };
下面是和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模塊內部一共有四類函數,關鍵的一類爲一類是動做處理函數,一共有:
這兩個函數經過在模塊初始化的過程當中,把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()函數中進行處理,一類爲與配置文件有關的建立和合並函數,一共有:
一類做爲common命令的解析函數,一共有:
一類爲解析命令中內部使用的函數,一共有: