本篇文章主要會(huì)介紹nginx中請(qǐng)求的接收流程,包括請(qǐng)求頭的解析和請(qǐng)求體的讀取流程。
首先介紹一下rfc2616中定義的http請(qǐng)求基本格式:
Request = Request-Line *(( general-header | request-header | entity-header ) CRLF) CRLF [ message-body ] </span>
第一行是請(qǐng)求行(request line),用來(lái)說(shuō)明請(qǐng)求方法,要訪問(wèn)的資源以及所使用的HTTP版本:
Request-Line = Method SP Request-URI SP HTTP-Version CRLF</span>
請(qǐng)求方法(Method)的定義如下,其中最常用的是GET,POST方法:
Method = "OPTIONS" | "GET" | "HEAD" | "POST" | "PUT" | "DELETE" | "TRACE" | "CONNECT" | extension-method extension-method = token
要訪問(wèn)的資源由統(tǒng)一資源地位符URI(Uniform Resource Identifier)確定,它的一個(gè)比較通用的組成格式(rfc2396)如下:
<scheme>://<authority><path>?<query>
一般來(lái)說(shuō)根據(jù)請(qǐng)求方法(Method)的不同,請(qǐng)求URI的格式會(huì)有所不同,通常只需寫出path和query部分。
http版本(version)定義如下,現(xiàn)在用的一般為1.0和1.1版本:
HTTP/<major>.<minor>
請(qǐng)求行的下一行則是請(qǐng)求頭,rfc2616中定義了3種不同類型的請(qǐng)求頭,分別為general-header,request-header和entity-header,每種類型rfc中都定義了一些通用的頭,其中entity-header類型可以包含自定義的頭。
現(xiàn)在開始介紹nginx中請(qǐng)求頭的解析,nginx的請(qǐng)求處理流程中,會(huì)涉及到2個(gè)非常重要的數(shù)據(jù)結(jié)構(gòu),ngx_connection_t和ngx_http_request_t,分別用來(lái)表示連接和請(qǐng)求,這2個(gè)數(shù)據(jù)結(jié)構(gòu)在本書的前篇中已經(jīng)做了比較詳細(xì)的介紹,沒有印象的讀者可以翻回去復(fù)習(xí)一下,整個(gè)請(qǐng)求處理流程從頭到尾,對(duì)應(yīng)著這2個(gè)數(shù)據(jù)結(jié)構(gòu)的分配,初始化,使用,重用和銷毀。
nginx在初始化階段,具體是在init process階段的ngx_event_process_init函數(shù)中會(huì)為每一個(gè)監(jiān)聽套接字分配一個(gè)連接結(jié)構(gòu)(ngx_connection_t),并將該連接結(jié)構(gòu)的讀事件成員(read)的事件處理函數(shù)設(shè)置為ngx_event_accept,并且如果沒有使用accept互斥鎖的話,在這個(gè)函數(shù)中會(huì)將該讀事件掛載到nginx的事件處理模型上(poll或者epoll等),反之則會(huì)等到init process階段結(jié)束,在工作進(jìn)程的事件處理循環(huán)中,某個(gè)進(jìn)程搶到了accept鎖才能掛載該讀事件。
static ngx_int_t ngx_event_process_init(ngx_cycle_t *cycle) { ... /* 初始化用來(lái)管理所有定時(shí)器的紅黑樹 */ if (ngx_event_timer_init(cycle->log) == NGX_ERROR) { return NGX_ERROR; } /* 初始化事件模型 */ for (m = 0; ngx_modules[m]; m++) { if (ngx_modules[m]->type != NGX_EVENT_MODULE) { continue; } if (ngx_modules[m]->ctx_index != ecf->use) { continue; } module = ngx_modules[m]->ctx; if (module->actions.init(cycle, ngx_timer_resolution) != NGX_OK) { /* fatal */ exit(2); } break; } ... /* for each listening socket */ /* 為每個(gè)監(jiān)聽套接字分配一個(gè)連接結(jié)構(gòu) */ ls = cycle->listening.elts; for (i = 0; i < cycle->listening.nelts; i++) { c = ngx_get_connection(ls[i].fd, cycle->log); if (c == NULL) { return NGX_ERROR; } c->log = &ls[i].log; c->listening = &ls[i]; ls[i].connection = c; rev = c->read; rev->log = c->log; /* 標(biāo)識(shí)此讀事件為新請(qǐng)求連接事件 */ rev->accept = 1; ... #if (NGX_WIN32) /* windows環(huán)境下不做分析,但原理類似 */ #else /* 將讀事件結(jié)構(gòu)的處理函數(shù)設(shè)置為ngx_event_accept */ rev->handler = ngx_event_accept; /* 如果使用accept鎖的話,要在后面搶到鎖才能將監(jiān)聽句柄掛載上事件處理模型上 */ if (ngx_use_accept_mutex) { continue; } /* 否則,將該監(jiān)聽句柄直接掛載上事件處理模型 */ if (ngx_event_flags & NGX_USE_RTSIG_EVENT) { if (ngx_add_conn(c) == NGX_ERROR) { return NGX_ERROR; } } else { if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) { return NGX_ERROR; } } #endif } return NGX_OK; }
新聞熱點(diǎn)
疑難解答
圖片精選