nginx HTTP處理流程
監聽套接字ngx_listenting_t->fd由獲取accept_mutex的worker進程加入epoll監控,其handler為ngx_event_accept;
注:每個fd賦予一個ngx_connection_t,且c->read->handler = ngx_event_accept(詳見ngx_event_process_init);
當客戶端發起新連接時,epoll_wait返回,將其加入accepted隊列,然后調用ngx_event_accept處理;
接受完客戶端連接后,立即調用ngx_listening_t->handler,即ngx_http_init_connection;
ngx_http_init_connection
- 將當前連接的讀事件revent->handler設置為ngx_http_init_request;
- 將當前連接的讀事件revent加入定時器,超時時間為client_header_timeout;
- 將當前連接的讀事件revent加入epoll監控;
- (注):當連接第一次出現可讀事件時,才會調用ngx_http_init_request
ngx_http_init_request
- 檢查是否超時client_header_timeout; 超時關閉鏈接。
- 創建ngx_http_request_t,找到監聽地址對應的sever,並設置這個請求對應的http配置項,
- (重新設置讀事件的回調方法)將revent->handler重置為ngx_http_process_request_line;
- 創建讀緩沖區client_header_buffer_size && ngx_http_request_t內存池(創建緩沖區內存池,初始化ngx_http_request_t結構體中的容器,創建指針數組以存放所有HTTP模板對該請求可能創建的上下文結構體,更新ngx_http_request_t 結構體的請求開始時間)
- 為ngx_http_request_t->ctx分配ngx_http_max_module個成員的指針數組(注1);
- 調用ngx_http_process_request_line;
注1: http請求上下文
Nginx采用全異步機制,一個http請求可能要多次調度http模塊才能完成,需要上下文結構體保存處理過程的中間狀態;
一個http請求對應每個http模塊都有一個獨立的上下文結構體,由ngx_http_request_t -> ** ctx保存;
每個模塊上下文結構體各異,通常在http請求第一次調用handler時分配;
Nginx提供兩個宏用於獲取和設置上下文
#define ngx_http_get_module_ctx(r,module) (r)->ctx[module.ctx_index]
#define ngx_http_set_ctx(r, c, module) r->ctx[module.ctx_index] = c;
ngx_http_process_request_line
- 接收請求行,格式為: 請求方法 uri http版本,(可調用多次)
- 執行完畢后將revent->handler重置為ngx_http_process_request_headers
ngx_http_process_request_headers
- 解析請求頭;
- 調用ngx_http_process_request處理http請求;
ngx_http_process_request
- 把讀事件從定時器移除,無需再接受http請求頭;
- 將當前連接讀事件c->read->handler設置為ngx_http_request_handler;(重新設置讀、寫事件的回調方法)
- 檢查ngx_http_request_t->internal,為1表示需要跳轉,將phase_handler改為server_rewrite_index,即調用NGX_HTTP_SERVER_REWRITE_PHASE階段的handler;
- 設置ngx_http_request_t->write_event_handler = ngx_http_core_run_phases;
- 調用ngx_http_core_run_phases;
- 執行post子請求;
ngx_http_read_client_request_body
- 接收請求body
ngx_http_send_header
ngx_http_output_filter
ngx_http_writer
接受請求
- ngx_http_close_connection
- ngx_http_free_request
- ngx_http_close_request
- ngx_http_finalize_connecion
- ngx_http_terminate_request
- ngx_http_finalize_request
問1:nginx擁有眾多http模塊,如何將其整合並確保http請求會用到相應模塊?
- nginx將http請求划分11個階段,每一階段可包含多個http模塊,每個模塊handler運行結果決定下一個模塊;
- 每個http階段由ngx_http_phase_handler_s描述,包含3個成員:checker,handler以及 next;
- Nginx不允許直接調用http模塊的handler,而是通過提供的checker封裝調用,共有7個http請求階段實現了checker(4個),也就是說只有這7個階段允許第3方模塊注冊;
* Nginx初始化時,調用每個http模塊的ngx_http_module_t->postconfiguration將自身的handler加入cmcf->phases(二維數組); - 然后通過ngx_http_init_phase_handlers()將cmcf->phases重組為一維數組cmcf->phase_engine.handlers,此時所有的ngx_http_phase_handler_s都位於其中;
- 一個http請求經過解析請求行和請求頭后,最終調用ngx_http_core_run_phases,其以http請求的phase_handler為下標,嘗試遍歷cmcf->phase_engine.handlers (可能因為處理結果提前返回)
ngx_http_core_run_phases(ngx_http_request_t *r)
{
ngx_int_t rc;
ngx_http_phase_handler_t *ph;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
ph = cmcf->phase_engine.handlers;
while (ph[r->phase_handler].checker) {
rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);
if (rc == NGX_OK) {
return;
}
}
}
以上是接受客戶端連接,並根據http請求行和請求頭執行相應http模塊,http請求可能還有包體,由http模塊負責處理;
一般有兩種方法: 1 接收包體並存入內存或文件; 2 接收包體后直接丟棄;