參考資料:深入理解Nginx(陶輝)
書中有詳細的講解,這里只用本人的理解梳理一下該流程。
一點提議:對於像我這樣的新手,面對暫時看不懂章節,建議先往下看一下(可能就會有新的理解或靈感),而不要死磕在某一章節。
幾個重要的數據結構
定義一個用於測試的結構體
我們的測試模塊將使用該結構體來存放配置信息,該結構只存放一個ngx_str_t。
typedef struct { ngx_str_t my_str; } ngx_http_mytest_conf_t;
先看看ngx_http_module_t的定義
typedef struct { ngx_int_t (*preconfiguration)(ngx_conf_t *cf); //解析配置文件前調用 ngx_int_t (*postconfiguration)(ngx_conf_t *cf); //完成配置文件解析后調用 void *(*create_main_conf)(ngx_conf_t *cf); //當需要創建數據結構用戶存儲main級別的全局配置項時候調用 char *(*init_main_conf)(ngx_conf_t *cf, void *conf); //初始化main級別配置項 void *(*create_srv_conf)(ngx_conf_t *cf); //當需要創建數據結構用戶存儲srv級別的全局配置項時候調用 char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf); //合並server級別的配置項 void *(*create_loc_conf)(ngx_conf_t *cf); //當需要創建數據結構用戶存儲loc級別的全局配置項時候調用 char *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf); //合並location級別的配置項 } ngx_http_module_t;
HTTP框架定義了3個級別的配置main、srv、loc,分別表示直接出現在http{}、server{}、location{}塊內的配置項。
例如:當解析遇到http{}配置塊時,會調用create_main_conf回調函數來創建並返回每個HTTP模塊對應的結構體(對於我們的測試模塊是ngx_http_mytest_conf_t)。
我們的mytest模塊實現create_loc_conf的是ngx_http_mytest_create_loc_conf方法
static void *ngx_http_mytest_create_loc_conf(ngx_conf_t *cf) { //創建mytest模塊對應的結構體ngx_http_mytest_conf; ngx_http_mytest_conf_t *mycf; mycf=(ngx_http_mytest_conf_t *)ngx_pcalloc(cf->pool,sizeof(ngx_http_mytest_conf_t)); if(mycf==NULL){ return NULL; } return mycf; }
那么Nginx是怎么保存這些配置信息的呢?
再看看ngx_http_conf_ctx_t結構
typedef struct { /* 指針數組,數組中的每個元素指向所有HTTP模塊create_main_conf方法產生的結構體*/ void **main_conf; /* 指針數組,數組中的每個元素指向所有HTTP模塊create_srv_conf方法產生的結構體*/ oid **srv_conf; /* 指針數組,數組中的每個元素指向所有HTTP模塊create_loc_conf方法產生的結構體*/ void **loc_conf; } ngx_http_conf_ctx_t;
用於定義模塊的配置參數的結構體ngx_command_t
typedef struct ngx_command_s ngx_command_t; 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:配置項的參數類型、參數個數以及配置項可以在哪些位置出現(http、server、location等)
set:處理配置項的回調方法。Nginx提供了14個預設的解析配置項的方法。
conf:對於HTTP模塊,conf是必須設置的,它的取值范圍見下表
offset:當前配置項在整個存儲配置項的結構體中的偏移位置。對於使用Nginx預設的解析方法:
Nginx首先通過conf成員找到應該用哪個結構體來存放,然后通過offset成員找到這個結構體中的相應成員,以便存放該配置。
在mytest模塊中,我們將這樣定義一個ngx_command_t數組
static ngx_command_t ngx_http_mytest_commands[] = { { ngx_string("test_str"), //配置項名稱 NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, //配置項只能出現在location塊中並且配置參數數量為1個 ngx_conf_set_str_slot, //使用預設的解析方法解析配置參數,表示我們希望用ngx_str_t類型的變量來保存這個配置項的參數 NGX_HTTP_LOC_CONF_OFFSET, //使用create_loc_conf方法產生的結構體來存儲解析出的配置項參數 offsetof(ngx_http_mytest_conf_t,my_str); //與上面兩個成員一起使用,通過這3個成員來找到該配置參數存放的位置(ngx_http_mytest_conf_t.my_str)。 NULL }, ngx_null_command };
HTTP配置的流程
1.主循環調用配置文件解析器解析nginx.conf文件。
2.當發現配置文件中含有http{}關鍵字時,HTTP框架開始啟動。
3.初始化所有HTTP模塊的序列號,並創建ngx_http_conf_ctx_t結構。
4.調用每個HTTP模塊的create_main_conf、create_srv_conf、create_loc_conf方法。
5.把各HTTP模塊上述3個方法返回的地址依次保存到ngx_http_conf_ctx_t結構體的3個數組中(如果某個模塊沒有定義相應的方法,則為NULL)。
6.調用每個HTTP模塊的preconfiguration方法。
7.如果preconfiguration返回失敗,那么Nginx進程將會停止。
8.HTTP框架開始循環解析nginx.conf文件中http{...}里面的所有配置項
9.配置文件解析器在檢測到一個配置項后,會遍歷所有HTTP模塊,檢查它們的ngx_command_t數組中的name項是否與配置項名相同。
10.如果找到一個HTTP模塊對這個配置項感興趣,就調用ngx_command_t結構中的set方法來處理該配置項。
11.如果set方法返回失敗,那么Nginx進程會停止。
12.配置文件解析器繼續檢查配置項。如果發現server{...}配置項,就會調用ngx_http_core_module模塊來處理。
13.ngx_http_core_module模塊在解析server{...}之前,也會如第三步一樣建立ngx_http_conf_ctx_t結構,並調用每個HTTP模塊的create_srv_conf、create_loc_conf回調方法。
14.將上一步各HTTP模塊返回的指針地址保存到ngx_http_conf_ctx_t對應的數組中。
15.開始調用配置文件解析器來處理server{...}里面的配置項。
16.繼續重復第9步的過程,遍歷nginx.conf中當前server{...}內的所有配置項。
17.配置文件解析器繼續解析配置項,如果發現當前server塊已經遍歷到尾部,則返回ngx_http_core_module模塊。
18.返回配置文件解析器繼續解析后面的配置項。
19.配置文件解析器繼續解析配置項,如果發現處理到了http{...}的尾部,返回個HTTP框架繼續處理。
20.調用merge_srv_conf、merge_loc_conf等方法合並這些不同塊中每個HTTP模塊分配的數據結構。
21.HTTP框架處理完畢http配置項,返回給配置文件解析器繼續處理其他http{...}外的配置項。
22.配置文件解析器處理完所有配置項后告訴Nginx主循環配置項解析完畢,這是Nginx才會啟動Web服務器。
注意:上面還有一些我們沒有列出來的步驟(如發現其他server塊或者location塊)。它們都會創建ngx_http_conf_ctx_t結構。
HTTP配置模型的內存布局
mytest模塊的完整代碼

1 #include <ngx_config.h> 2 #include <ngx_core.h> 3 #include <ngx_http.h> 4 5 typedef struct{ 6 ngx_str_t my_str; 7 }ngx_http_mytest_conf_t; 8 9 static ngx_int_t 10 ngx_http_mytest_handler(ngx_http_request_t *r); 11 12 //創建mytest模塊對應的結構體ngx_http_mytest_conf; 13 static void *ngx_http_mytest_create_loc_conf(ngx_conf_t *cf) 14 { 15 ngx_http_mytest_conf_t *mycf; 16 mycf=(ngx_http_mytest_conf_t *)ngx_pcalloc(cf->pool,sizeof(ngx_http_mytest_conf_t)); 17 if(mycf==NULL){ 18 return NULL; 19 } 20 return mycf; 21 } 22 23 static ngx_command_t ngx_http_mytest_commands[] = { 24 { 25 ngx_string("response_line"), //配置項名稱 26 NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, //配置項只能出現在location塊中並且配置參數數量為1個 27 ngx_conf_set_str_slot, //使用預設的解析方法解析配置參數,表示我們希望用ngx_str_t類型的變量來保存這個配置項的參數 28 NGX_HTTP_LOC_CONF_OFFSET, //使用create_loc_conf方法產生的結構體來存儲解析出的配置項參數 29 offsetof(ngx_http_mytest_conf_t,my_str), //與上面兩個成員一起使用,通過這3個成員來找到該配置參數存放的位置(ngx_http_mytest_conf_t.my_str)。 30 NULL 31 }, 32 ngx_null_command 33 }; 34 35 //掛載handler 36 static ngx_int_t 37 ngx_http_mytest_init(ngx_conf_t *cf) 38 { 39 ngx_http_handler_pt *h; 40 ngx_http_core_main_conf_t *cmcf; 41 42 cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); 43 44 h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers); 45 if (h == NULL) { 46 return NGX_ERROR; 47 } 48 49 *h = ngx_http_mytest_handler; 50 51 return NGX_OK; 52 } 53 54 static ngx_http_module_t ngx_http_mytest_module_ctx={ 55 NULL, 56 ngx_http_mytest_init, 57 NULL, 58 NULL, 59 NULL, 60 NULL, 61 ngx_http_mytest_create_loc_conf, 62 NULL 63 }; 64 65 ngx_module_t ngx_http_mytest_module={ 66 NGX_MODULE_V1, 67 //指向ngx_http_module_t結構體 68 &ngx_http_mytest_module_ctx, 69 //用來處理nginx.conf中的配置項 70 ngx_http_mytest_commands, 71 //表示該模塊的類型 72 NGX_HTTP_MODULE, 73 NULL, 74 NULL, 75 NULL, 76 NULL, 77 NULL, 78 NULL, 79 NULL, 80 NGX_MODULE_V1_PADDING 81 }; 82 83 static ngx_int_t ngx_http_mytest_handler(ngx_http_request_t *r) 84 { 85 //判斷請求方法是否為GET或者HEAD,否則返回405 Not Allowed 86 if(!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))){ 87 return NGX_HTTP_NOT_ALLOWED; 88 } 89 90 //丟棄請求中的包體 91 ngx_int_t rc=ngx_http_discard_request_body(r); 92 if(rc!=NGX_OK){ 93 return rc; 94 } 95 96 //設置相應頭 97 ngx_str_t type=ngx_string("text/plain"); 98 //獲取配置參數 99 ngx_http_mytest_conf_t* my_conf; 100 my_conf = ngx_http_get_module_loc_conf(r, ngx_http_mytest_module); 101 ngx_str_t response=my_conf->my_str; 102 r->headers_out.status=NGX_HTTP_OK; 103 r->headers_out.content_length_n=response.len; 104 r->headers_out.content_type=type; 105 106 //發送HTTP頭部 107 rc=ngx_http_send_header(r); 108 if(rc==NGX_ERROR||rc>NGX_OK||r->header_only){ 109 return rc; 110 } 111 112 //構造ngx_buf_t結構體准備發送包體 113 ngx_buf_t *b; 114 b=ngx_create_temp_buf(r->pool,response.len); 115 if(b==NULL){ 116 return NGX_HTTP_INTERNAL_SERVER_ERROR; 117 } 118 ngx_memcpy(b->pos,response.data,response.len); 119 b->last=b->pos+response.len; 120 b->last_buf=1; 121 122 //構造發送時的ngx_chain_t結構體 123 ngx_chain_t out; 124 out.buf=b; 125 out.next=NULL; 126 return ngx_http_output_filter(r,&out); 127 }
按照http://www.cnblogs.com/runnyu/p/4871866.html編譯安裝該模塊
在nginx.conf文件中默認server塊下配置如下信息
location /response {
response_line HelloWorld\r\n;
}
使用telnet查看結果