2、HTTP框架
繼續上面這個例子,比如當nginx收到一個http請求時,我的module需要處理這個請求,那么我應該怎么做?實際這個問題還要再細分。如果是希望nginx收到完整的HTTP請求,再交給我的module處理?又或者只需要接收到完整的http header就給我呢?我把接收完header就交給module處理的code列下,再說下它的處理流程。
首先我要在ngx_XXX_init里注冊對這種請求的處理函數。
~~~
static char * ngx_XXX_init(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
????????//... code 省略
????????
????????clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
????????clcf->handler = ngx_XXX_handler;
?
????????return NGX_CONF_OK;
}
~~~
好了,現在有個簡單的GET請求進入后,nginx在接收完header,就來調用ngx_XXX_handler方法了。好,現在我們調試下,看看nginx是如何進入ngx_XXX_handler方法的。
~~~
#0?ngx_XXX_handler (r=0x6d5650)
???at nginx_XXX_module/ngx_XXX_module.c:646
#1?0x0000000000429e77 in ngx_http_core_content_phase (r=0x6d5650, ph=0x6e17d8)
???at src/http/ngx_http_core_module.c:1262
#2?0x00000000004292d0 in ngx_http_core_run_phases (r=0x6d5650)
???at src/http/ngx_http_core_module.c:800
#3?0x0000000000429284 in ngx_http_handler (r=0x6d5650)
???at src/http/ngx_http_core_module.c:783
#4?0x000000000043165c in ngx_http_process_request (r=0x6d5650)
???at src/http/ngx_http_request.c:1615
#5?0x0000000000430845 in ngx_http_process_request_headers (rev=0x6d60d0)
???at src/http/ngx_http_request.c:1064
#6?0x00000000004303a0 in ngx_http_process_request_line (rev=0x6e3730)
???at src/http/ngx_http_request.c:869
#7?0x000000000042fa8a in ngx_http_init_request (rev=0x6e3730)
???at src/http/ngx_http_request.c:510
#8?0x0000000000422d6f in ngx_epoll_process_events (cycle=0x6cb1d0, timer=Variable "timer" is not available.
)
???at src/event/modules/ngx_epoll_module.c:518
#9?0x00000000004181cb in ngx_process_events_and_timers (cycle=0x6cb1d0)
???at src/event/ngx_event.c:245
#10 0x0000000000420351 in ngx_worker_process_cycle (cycle=0x6cb1d0, data=Variable "data" is not available.
)
???at src/os/unix/ngx_process_cycle.c:791
#11 0x000000000041e19b in ngx_spawn_process (cycle=0x6cb1d0,
???proc=0x420271 <ngx_worker_process_cycle>, data=0x0, name=0x5171a4 "worker process",
???respawn=-3) at src/os/unix/ngx_process.c:194
#12 0x000000000041f8df in ngx_start_worker_processes (cycle=0x6cb1d0, n=1, type=-3)
???at src/os/unix/ngx_process_cycle.c:355
#13 0x000000000041f24d in ngx_master_process_cycle (cycle=0x6cb1d0)
???at src/os/unix/ngx_process_cycle.c:136
#14 0x0000000000403dea in main (argc=1, argv=Variable "argv" is not available.
) at src/core/nginx.c:396
~~~
?
上面這個棧信息,可以初步看到nginx是如果調用ngx_XXX_handler,我再詳細說下。
ngx_epoll_process_events接收到網絡IO事件EPOLLIN后(socket上有數據可讀),rev->handler(rev);調用了這個回調方法(ngx_http_init_request)。ngx_http_init_request開始處理這個事件,首先它把基本的ngx_http_request_t變量(nginx HTTP框架中由始至終的東東)初始化,并從內存池中分配了第一塊接收內存,然后開始調用ngx_http_process_request_line方法處理具體內容。
ngx_http_process_request_line開始處理具體接收到的消息了,它首先會調用ngx_http_read_request_header方法去讀取socket上的字節,讀完后(僅僅是當前socket上的緩沖)調用ngx_http_parse_request_line方法分析協議,ngx_http_parse_request_line方法就是一個HTTP協議的狀態機實現,分析HTTP協議并賦值到ngx_http_request_t相應的字段里,如果不完整,就返回epoll等待這個socket上的下一次EPOLLIN事件。如果HTTP協議頭完整了,就開始調用ngx_http_process_request_headers處理http header的內容了。
ngx_http_process_request_headers方法會先調用ngx_http_parse_header_line去分析http header。ngx_http_parse_header_line也是一個狀態機,僅用來解析http header,之后開始調用ngx_http_process_request方法處理已經解析成功的ngx_http_request_t對象。ngx_http_process_request簡單的調用ngx_http_handler方法,ngx_http_handler方法把事件發布到ngx_http_core_run_phases階段,開始回調我們在ngx_XXX_init方法中注冊的ngx_XXX_handle方法。
現在大家清楚一個最基本的module處理流程是怎樣的了吧?如果大家還是不大明白,我畫個活動圖幫助大家理解下:
