nginx長(zhǎng)連接keepalive與pipeline使用及說(shuō)明
tcp與http都支持keepalive機(jī)制,但兩者是不同的。先看下tcp的keepalive機(jī)制。當(dāng)客戶端與服務(wù)器建立了tcp連接后,如果客戶端一直不發(fā)送數(shù)據(jù),或者隔很長(zhǎng)時(shí)間才發(fā)送一次數(shù)據(jù)。當(dāng)連接很久沒(méi)有數(shù)據(jù)報(bào)文傳輸時(shí),服務(wù)器如何去確定對(duì)方還在線。到底是掉線了還是確實(shí)沒(méi)有數(shù)據(jù)傳輸,連接還需不需要保持,這種情況在TCP協(xié)議設(shè)計(jì)中是需要考慮的。TCP協(xié)議通過(guò)一種巧妙的方式去解決這個(gè)問(wèn)題,當(dāng)超過(guò)一段時(shí)間之后,TCP自動(dòng)發(fā)送一個(gè)數(shù)據(jù)為空的報(bào)文給對(duì)方,如果對(duì)方回應(yīng)了這個(gè)報(bào)文,說(shuō)明對(duì)方還在線,連接可以繼續(xù)保持,如果對(duì)方?jīng)]有報(bào)文返回并且重試了多次之后則認(rèn)為連接丟失,沒(méi)有必要保持連接。這個(gè)過(guò)程相當(dāng)于服務(wù)器向客戶端發(fā)送心跳包,確認(rèn)客戶端是否還在線。
而http的keepalive機(jī)制為:通??蛻舳藶g覽器要展現(xiàn)一張完整的頁(yè)面需要很多個(gè)請(qǐng)求才能完成,如圖片,js,CSS等。如果每一個(gè)HTTP請(qǐng)求都需要新建并斷開(kāi)一個(gè)TCP,這個(gè)開(kāi)銷(xiāo)是完全沒(méi)有必要的。開(kāi)啟HTTP Keep-Alive之后,能復(fù)用已有的TCP鏈接, 當(dāng)一個(gè)請(qǐng)求已經(jīng)響應(yīng)完畢,服務(wù)器端沒(méi)有立即關(guān)閉TCP連接,而是等待一段時(shí)間繼續(xù)接收瀏覽器可能發(fā)送過(guò)來(lái)的第二個(gè)請(qǐng)求,通常瀏覽器在第一個(gè)請(qǐng)求返回之后會(huì)立即發(fā)送第二個(gè)請(qǐng)求。因此在一個(gè)tcp連接上可以存在多個(gè)http請(qǐng)求,當(dāng)然這個(gè)如果客戶端一直不發(fā)送新的http請(qǐng)求,超過(guò)一段時(shí)間后,nginx服務(wù)器還是會(huì)關(guān)閉這個(gè)TCP長(zhǎng)連接。
開(kāi)啟keepalive功能,可以減少time-wait套接字的個(gè)數(shù)
一、keealive的請(qǐng)求頭部解析
在http1.0協(xié)議里,需要客戶端發(fā)送connection: keep-alive請(qǐng)求頭來(lái)實(shí)現(xiàn)與服務(wù)器之間的長(zhǎng)連接,否則長(zhǎng)連接默認(rèn)是關(guān)閉的。 http1.1默認(rèn)支持keepalive,也就是說(shuō)即使客戶端沒(méi)有發(fā)送connection:keep_alive請(qǐng)求頭部,服務(wù)器也會(huì)設(shè)置keepalive屬性,然后等待客戶端下一次請(qǐng)求。但通過(guò)請(qǐng)求頭connection: close可明確要求不進(jìn)行長(zhǎng)連接保持。先看下如何設(shè)置keepalive。
//http請(qǐng)求頭部常用頭部對(duì)應(yīng)的處理函數(shù)
ngx_http_header_t ngx_http_headers_in[] =
{
{ ngx_string("Connection"), offsetof(ngx_http_headers_in_t, connection),ngx_http_process_connection },
};//處理http請(qǐng)求頭部的connection字段
static ngx_int_t ngx_http_process_connection(ngx_http_request_t *r, ngx_table_elt_t *h,
ngx_uint_t offset)
{
if (ngx_strcasestrn(h->value.data, "close", 5 - 1))
{
//長(zhǎng)連接關(guān)閉
r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE;
}
else if (ngx_strcasestrn(h->value.data, "keep-alive", 10 - 1))
{
//保持長(zhǎng)連接
r->headers_in.connection_type = NGX_HTTP_CONNECTION_KEEP_ALIVE;
}
return NGX_OK;
}在解析http頭部的connection字段后,將會(huì)設(shè)置TCP的連接類(lèi)型,是打開(kāi)長(zhǎng)連接還是關(guān)閉長(zhǎng)連接。然后會(huì)設(shè)置到請(qǐng)求對(duì)象的keepalive中。對(duì)于keepalive只用于服務(wù)器與客戶端保持TCP長(zhǎng)連接,而nginx內(nèi)部操作,例如內(nèi)部跳轉(zhuǎn), 命名location跳轉(zhuǎn)、子請(qǐng)求等是不需要用到keepalive的。
//調(diào)用各個(gè)http模塊協(xié)同處理這個(gè)請(qǐng)求
void ngx_http_handler(ngx_http_request_t *r)
{
//不需要進(jìn)行內(nèi)部跳轉(zhuǎn)。keepalive機(jī)制是在客戶端和nginx服務(wù)器之間才需要關(guān)注。對(duì)于內(nèi)部跳轉(zhuǎn)則不會(huì)用到
//keepalive機(jī)制
if (!r->internal)
{
switch (r->headers_in.connection_type)
{
case 0:
r->keepalive = (r->http_version > NGX_HTTP_VERSION_10);//http1.1版本默認(rèn)開(kāi)啟keepalive
break;
case NGX_HTTP_CONNECTION_CLOSE:
r->keepalive = 0; //關(guān)閉長(zhǎng)連接
break;
case NGX_HTTP_CONNECTION_KEEP_ALIVE:
r->keepalive = 1; //打開(kāi)長(zhǎng)連接
break;
}
}
}二、keepalive的設(shè)置
當(dāng)一個(gè)http請(qǐng)求完成后, 這個(gè)時(shí)候引用計(jì)數(shù)為0了,會(huì)釋放這個(gè)http請(qǐng)求,但到底要不要釋放tcp連接,是由keepalive機(jī)制與延遲關(guān)閉機(jī)制決定的。keepalive相比延遲關(guān)閉,優(yōu)先級(jí)更高。來(lái)看下ngx_http_finalize_connection函數(shù)的實(shí)現(xiàn)。
//釋放http請(qǐng)求與連接
static void ngx_http_finalize_connection(ngx_http_request_t *r)
{
//執(zhí)行到這里,引用計(jì)數(shù)為1,則要準(zhǔn)備結(jié)束請(qǐng)求了
//keepalive為1表示請(qǐng)求需要釋放,但tcp連接還是用復(fù)用的
if (!ngx_terminate
&& !ngx_exiting
&& r->keepalive
&& clcf->keepalive_timeout > 0)
{
ngx_http_set_keepalive(r);
return;
}
}keepalive_timeout可以在nginx.conf配置文件中,通過(guò)keepalive_timeout指令設(shè)置,默認(rèn)情況下超時(shí)時(shí)間就是75秒。如果超過(guò)這個(gè)時(shí)間都還沒(méi)有收到來(lái)自客戶端新的http請(qǐng)求,則會(huì)關(guān)閉這個(gè)tcp連接,keepalive功能也就結(jié)束了。ngx_http_set_keepalive函數(shù)則是正式開(kāi)始keepalive的處理,函數(shù)有點(diǎn)長(zhǎng),實(shí)現(xiàn)了pipeline長(zhǎng)連接與普通長(zhǎng)連接。先來(lái)看下pipeline的實(shí)現(xiàn)。
三、pipeline處理
那么什么是pipeline呢?pipeline其實(shí)就是流水線作業(yè),它可以看作為keepalive的一種升華,因?yàn)閜ipeline也是基于長(zhǎng)連接的,目的就是利用一個(gè)連接做多次請(qǐng)求。如果客戶端要提交多個(gè)請(qǐng)求,對(duì)于keepalive來(lái)說(shuō),那么第二個(gè)請(qǐng)求,必須要等到第一個(gè)請(qǐng)求的響應(yīng)接收完全后,才能發(fā)起,這和TCP的停止等待協(xié)議是一樣的,得到兩個(gè)響應(yīng)的時(shí)間至少為2*RTT。而對(duì)pipeline來(lái)說(shuō),客戶端不必等到第一個(gè)請(qǐng)求處理完后,就可以馬上發(fā)起第二個(gè)請(qǐng)求。得到兩個(gè)響應(yīng)的時(shí)間可能能夠達(dá)到1*RTT。nginx是直接支持pipeline的,但是,nginx對(duì)pipeline中的多個(gè)請(qǐng)求的處理卻不是并行的,依然是一個(gè)請(qǐng)求接一個(gè)請(qǐng)求的處理,只是在處理第一個(gè)請(qǐng)求的時(shí)候,客戶端就可以發(fā)起第二個(gè)請(qǐng)求。這樣,nginx利用pipeline減少了處理完一個(gè)請(qǐng)求后,等待第二個(gè)請(qǐng)求的請(qǐng)求行與請(qǐng)求頭部的時(shí)間。其實(shí)nginx的做法很簡(jiǎn)單,前面說(shuō)到,nginx在讀取數(shù)據(jù)時(shí),會(huì)將讀取的數(shù)據(jù)放到一個(gè)buffer里面,所以,如果nginx在處理完前一個(gè)請(qǐng)求后,如果發(fā)現(xiàn)buffer里面還有數(shù)據(jù),就認(rèn)為剩下的數(shù)據(jù)是下一個(gè)請(qǐng)求的開(kāi)始,然后接下來(lái)處理下一個(gè)請(qǐng)求,否則就設(shè)置keepalive。
來(lái)看下nginx服務(wù)器是如何處理pipeline的?
//設(shè)置keepalive過(guò)程
static void ngx_http_set_keepalive(ngx_http_request_t *r)
{
//在接收到來(lái)自客戶端的連接請(qǐng)求時(shí),同時(shí)也接收到了同一個(gè)tcp連接上的第2個(gè)http請(qǐng)求頭部。
//因此在處理完第一個(gè)請(qǐng)求時(shí),pos不等于last,說(shuō)明接收到了第二個(gè)http請(qǐng)求頭部,接下來(lái)要立馬處理第二個(gè)請(qǐng)求
if (b->pos < b->last)
{
//在接收來(lái)自客戶端的請(qǐng)求行、或者請(qǐng)求頭部時(shí)。如果連接對(duì)象的buffer緩沖區(qū)不能夠存放所有的請(qǐng)求行,請(qǐng)求頭。
//則http請(qǐng)求對(duì)象自己會(huì)開(kāi)辟新的空間。因此在請(qǐng)求結(jié)束時(shí),需要把請(qǐng)求對(duì)象開(kāi)辟的空間加入到空閑表中。
//這樣在這個(gè)連接上有新的http請(qǐng)求到來(lái)時(shí),可以復(fù)用上一個(gè)請(qǐng)求對(duì)象開(kāi)辟的空間
if (b != c->buffer)
{
if (hc->free == NULL)
{
hc->free = ngx_palloc(c->pool, cscf->large_client_header_buffers.num * sizeof(ngx_buf_t *));
}
//將在用表移動(dòng)到空閑表
for (i = 0; i < hc->nbusy - 1; i++)
{
f = hc->busy[i];
hc->free[hc->nfree++] = f;
f->pos = f->start;
f->last = f->start;
}
hc->busy[0] = b;
hc->nbusy = 1;
}
}
}(1) nginx服務(wù)器是怎么知道需要進(jìn)行pipeline處理呢? 條件就是這個(gè)b->pos < b->last。 多個(gè)http請(qǐng)求可以復(fù)用同一個(gè)tcp連接,客戶端瀏覽器在發(fā)出第一個(gè)http請(qǐng)求時(shí),可以不需要等待收到服務(wù)器的響應(yīng),可以立馬發(fā)出第二個(gè)http請(qǐng)求。在這種請(qǐng)求下,nginx服務(wù)器收到第一個(gè)http請(qǐng)求時(shí),是有可能也接收到了第二個(gè)http請(qǐng)求的頭部信息,并保存到http請(qǐng)求對(duì)象的header_in緩沖區(qū)中??聪逻@個(gè)接收過(guò)程, 函數(shù)只負(fù)責(zé)從內(nèi)核讀取數(shù)據(jù)到緩沖區(qū),并沒(méi)有限制只讀取第一個(gè)http請(qǐng)求的數(shù)據(jù),如果有第二個(gè)http請(qǐng)求,也會(huì)把第二個(gè)http請(qǐng)求頭部也讀取到緩沖區(qū)。
static ssize_t ngx_http_read_request_header(ngx_http_request_t *r)
{
//事件就緒,也就是從epoll_wait中返回后,從內(nèi)核緩沖區(qū)中讀取內(nèi)容到應(yīng)用層緩沖區(qū)header_in
if (rev->ready)
{
//ngx_unix_recv
n = c->recv(c, r->header_in->last, r->header_in->end - r->header_in->last);
}
}那這個(gè)b != c->buffer又怎么理解呢? 默認(rèn)情況下http請(qǐng)求結(jié)構(gòu)的header_in緩沖區(qū)是等于連接對(duì)象的buffer緩沖區(qū)的。 看下面這個(gè)函數(shù)就知道了。剛建立http請(qǐng)求時(shí),兩個(gè)緩沖區(qū)都指向同一個(gè)內(nèi)存空間。
//首次建立tcp連接后,讀事件的回調(diào),用于創(chuàng)建一個(gè)ngx_http_request_t對(duì)象
static void ngx_http_init_request(ngx_event_t *rev)
{
//剛建立的請(qǐng)求,默認(rèn)情況下這兩個(gè)緩沖區(qū)是相等的
if (r->header_in == NULL)
{
r->header_in = c->buffer;
}
}那什么情況下http請(qǐng)求結(jié)構(gòu)的header_in緩沖區(qū)會(huì)不等于連接對(duì)象的buffer緩沖區(qū)呢? nginx服務(wù)器在接收到來(lái)自客戶端的http請(qǐng)求行或者請(qǐng)求頭部時(shí),如果接受緩沖區(qū)不能夠存放請(qǐng)求行或者請(qǐng)求頭時(shí),是會(huì)開(kāi)辟一個(gè)新的緩沖區(qū),并把這個(gè)緩沖區(qū)加入到在用緩沖區(qū)數(shù)組busy中。通常情況下nginx服務(wù)器的默認(rèn)緩沖區(qū)是足夠存放一個(gè)http請(qǐng)求行或者請(qǐng)求頭部的, 之所以緩沖區(qū)不夠,大部分情況下是接收到了多個(gè)http請(qǐng)求頭部。這種情況下將會(huì)執(zhí)行pipeline處理
//開(kāi)辟一個(gè)大的緩沖區(qū),并把舊緩沖區(qū)的數(shù)據(jù)拷貝到新緩沖區(qū)中
//解析請(qǐng)求行時(shí),request_line = 1, 解析請(qǐng)求頭時(shí),request_line = 0
static ngx_int_t ngx_http_alloc_large_header_buffer(ngx_http_request_t *r, ngx_uint_t request_line)
{
/* 檢查給該請(qǐng)求分配的請(qǐng)求頭緩沖區(qū)個(gè)數(shù)是否已經(jīng)超過(guò)限制,默認(rèn)最大個(gè)數(shù)為4個(gè) */
if (hc->busy == NULL)
{
hc->busy = ngx_palloc(r->connection->pool, cscf->large_client_header_buffers.num * sizeof(ngx_buf_t *));
}
/* 如果還沒(méi)有達(dá)到最大分配數(shù)量,則分配一個(gè)新的大緩沖區(qū) */
b = ngx_create_temp_buf(r->connection->pool, cscf->large_client_header_buffers.size);
/* 將從空閑隊(duì)列取得的或者新分配的緩沖區(qū)加入已使用隊(duì)列 */
hc->busy[hc->nbusy++] = b;
//header_in指向這個(gè)新的緩沖區(qū), 連接對(duì)象的緩沖區(qū)仍然為舊的緩沖區(qū),大小沒(méi)有改變
r->header_in = b;
}為什么要把busy這個(gè)在用緩沖區(qū)數(shù)據(jù)移動(dòng)到空閑緩沖區(qū)數(shù)組呢? 為什么不直接釋放這些空間呢? 還是因?yàn)閜ipeline的原因,因?yàn)槌水?dāng)前要結(jié)束的這個(gè)http請(qǐng)求外,緩沖區(qū)中還存放了其它的http請(qǐng)求。當(dāng)這個(gè)http請(qǐng)求結(jié)束時(shí),可以立即處理剩余的http請(qǐng)求。而如果不是pipeline,則會(huì)關(guān)閉這些緩沖區(qū)的, 這種情況下是一個(gè)普通的TCP長(zhǎng)連接,客戶端什么時(shí)候會(huì)再次發(fā)送http請(qǐng)求,以及是否真的還會(huì)在發(fā)送http請(qǐng)求是不可知的, nginx將會(huì)釋放這些資源。
(2)接下來(lái)要釋放一個(gè)http請(qǐng)求了,但請(qǐng)求本身這個(gè)對(duì)象沒(méi)有釋放, 這樣在這個(gè)tcp連接上的已經(jīng)接收到的其它請(qǐng)求可以復(fù)用這個(gè)http請(qǐng)求對(duì)象。同時(shí)也會(huì)設(shè)置keepalive的超時(shí)時(shí)間,超過(guò)這個(gè)時(shí)間后會(huì)真正關(guān)閉這個(gè)tcp連接。
//設(shè)置keepalive過(guò)程
static void ngx_http_set_keepalive(ngx_http_request_t *r)
{
//釋放請(qǐng)求的內(nèi)容空間
ngx_http_free_request(r, 0);
//將讀事件注冊(cè)到紅黑樹(shù)實(shí)現(xiàn)的定時(shí)器中,超時(shí)時(shí)間由keepalive_timeout命令設(shè)置,默認(rèn)為75秒。
//如果超時(shí)時(shí)間到后都還沒(méi)有再收到來(lái)自客戶端的http請(qǐng)求,則會(huì)關(guān)閉連接。
ngx_add_timer(rev, clcf->keepalive_timeout);
ngx_handle_read_event(rev, 0);
//接收來(lái)自客戶端請(qǐng)求時(shí),還不需要向客戶端寫(xiě)入數(shù)據(jù),因此把寫(xiě)回調(diào)設(shè)置為不做任何事情
wev = c->write;
wev->handler = ngx_http_empty_handler;
}(3)在pipeline這種情況下,當(dāng)前http請(qǐng)求已經(jīng)結(jié)束了。那nginx如何處理這個(gè)tcp連接上的其它http請(qǐng)求呢? nginx服務(wù)器將會(huì)設(shè)置讀事件ngx_event_t的接收回調(diào)handler為:ngx_http_init_request,并立馬把讀事件加入到post隊(duì)列中。因此這個(gè)讀事件就會(huì)被立即調(diào)用,從而開(kāi)始處理這個(gè)tcp連接上的其它http請(qǐng)求。
static void ngx_http_set_keepalive(ngx_http_request_t *r)
{
//該請(qǐng)求結(jié)束了,設(shè)置接收回調(diào)為ngx_http_init_request。在這個(gè)tcp連接上的下一個(gè)請(qǐng)求到來(lái)時(shí),
//重新開(kāi)始對(duì)一個(gè)新請(qǐng)求進(jìn)行處理
if (b->pos < b->last)
{
//設(shè)置為串行請(qǐng)求
hc->pipeline = 1;
//設(shè)置讀事件的回調(diào),并加入到post事件隊(duì)列
rev->handler = ngx_http_init_request;
ngx_post_event(rev, &ngx_posted_events);
return;
}
}看下ngx_http_init_request這個(gè)函數(shù)怎么在這個(gè)tcp連接上重新創(chuàng)建一個(gè)http請(qǐng)求。對(duì)于pipeline,是不會(huì)創(chuàng)建一個(gè)新的http請(qǐng)求的,而是復(fù)用已經(jīng)結(jié)束的http請(qǐng)求。也可以看出不管是在這個(gè)tcp連接上新建立的http請(qǐng)求,還是當(dāng)前請(qǐng)求結(jié)束后重新建立了一個(gè)新的http請(qǐng)求,都會(huì)調(diào)用ngx_http_init_request這個(gè)函數(shù)進(jìn)行處理。
static void ngx_http_init_request(ngx_event_t *rev)
{
r = hc->request;
if (r)
{
ngx_memzero(r, sizeof(ngx_http_request_t));
//串行請(qǐng)求時(shí)值為1,表示舊請(qǐng)求結(jié)束后,內(nèi)部資源已經(jīng)釋放了,但請(qǐng)求本身沒(méi)有釋放。
//這樣的話,在這個(gè)tcp連接上的新請(qǐng)求可以使用舊請(qǐng)求的對(duì)象。
//配和ngx_http_set_keepalive函數(shù)就理解這部分的實(shí)現(xiàn)了
r->pipeline = hc->pipeline;
//串行請(qǐng)求時(shí),在用緩沖區(qū)加入到了空閑緩沖區(qū)數(shù)組中。配和ngx_http_set_keepalive函數(shù)幫助理解
if (hc->nbusy)
{
r->header_in = hc->busy[0];
}
}
}需要注意的是pipeline只支持冪等請(qǐng)求,也就是并發(fā)請(qǐng)求之間沒(méi)有依賴關(guān)系,不管執(zhí)行多少次結(jié)果都應(yīng)該是一樣的。例如在第二個(gè)請(qǐng)求需要等待第一個(gè)請(qǐng)求的響應(yīng)從而決定一些行為時(shí),這種場(chǎng)景下就不能使用pipeline。第一個(gè)請(qǐng)求添加用戶信息,第二個(gè)請(qǐng)求更新用戶信息,這種場(chǎng)景下就不能使用pipeline。當(dāng)然這需要由客戶端自己來(lái)保證,并發(fā)發(fā)送多個(gè)冪等性的請(qǐng)求。
到此對(duì)pipeline這種長(zhǎng)連接的處理過(guò)程已經(jīng)分析完成了,下面將分析下非pipeline情況下的長(zhǎng)連接的處理過(guò)程。暫且稱(chēng)非pipeline的長(zhǎng)連接為普通長(zhǎng)連接吧!
四、普通長(zhǎng)連接處理
如果不是pipeline這種串行請(qǐng)求,因此會(huì)盡可能的釋放該請(qǐng)求空間。因?yàn)檫@個(gè)tcp連接上什么時(shí)候會(huì)有客戶端發(fā)來(lái)新的http請(qǐng)求,以及是否真的會(huì)有新的http請(qǐng)求是不確定的。nginx秉承一個(gè)能盡量減少資源占用就減少資源的原則,會(huì)把這個(gè)http請(qǐng)求內(nèi)部開(kāi)辟的資源給釋放,同時(shí)也把請(qǐng)求對(duì)象本身也真正的釋放, 但tcp連接還是沒(méi)有關(guān)閉,等超時(shí)沒(méi)有收到來(lái)自客戶端的http請(qǐng)求時(shí)在關(guān)閉。以此同時(shí)將會(huì)設(shè)置讀事件ngx_event_t的接收回調(diào)handler為:ngx_http_keepalive_handler,用來(lái)處理在這個(gè)tcp連接上的其它http請(qǐng)求(這里不像pipeline, 當(dāng)前http請(qǐng)求結(jié)束了,但此時(shí)這個(gè)tcp連接上并沒(méi)有其它的http請(qǐng)求存在)。
static void ngx_http_set_keepalive(ngx_http_request_t *r)
{
//釋放資源
if (hc->busy)
{
for (i = 0; i < hc->nbusy; i++)
{
ngx_pfree(c->pool, hc->busy[i]->start);
hc->busy[i] = NULL;
}
}
//設(shè)置讀事件回調(diào),用于處理來(lái)自客戶端的新的http請(qǐng)求
rev->handler = ngx_http_keepalive_handler;
}現(xiàn)在來(lái)看是ngx_http_keepalive_handler這個(gè)函數(shù)的處理過(guò)程。函數(shù)內(nèi)部會(huì)開(kāi)辟一些必要的緩沖區(qū)外,最終還是回調(diào)用ngx_http_init_request這個(gè)函數(shù)重新開(kāi)始一個(gè)新的http請(qǐng)求。
//http請(qǐng)求關(guān)閉后,tcp連接并沒(méi)有關(guān)閉。這個(gè)函數(shù)用于在這個(gè)tcp連接上接收新的http請(qǐng)求
static void ngx_http_keepalive_handler(ngx_event_t *rev)
{
//開(kāi)辟接收請(qǐng)求行、請(qǐng)求頭緩沖區(qū)。因?yàn)榉莗ipeline情況下,釋放之前的一個(gè)請(qǐng)求時(shí),
//也把連接對(duì)象的buffer緩沖區(qū)也給釋放了。因此新的請(qǐng)求到來(lái)時(shí),需要重新開(kāi)辟空間
b->pos = ngx_palloc(c->pool, size);
//調(diào)用這個(gè)函數(shù),重新開(kāi)始一個(gè)新的http請(qǐng)求處理
ngx_http_init_request(rev);
}可以看出,在同一個(gè)tcp連接下,不管是第一次建立的請(qǐng)求,還是之后重新建立的請(qǐng)求,最終都會(huì)調(diào)用ngx_http_init_request這個(gè)函數(shù)。由這個(gè)函數(shù)負(fù)責(zé)對(duì)請(qǐng)求的初始化操作。由此也可以知道,多個(gè)http請(qǐng)求是可以復(fù)用同一個(gè)tcp連接的。沒(méi)有必要客戶端發(fā)起一個(gè)http請(qǐng)求就建立一個(gè)tcp連接,太浪費(fèi)資源了。到此keepalive機(jī)制與pipeline已經(jīng)分析完成了, 因?yàn)閗eepalive機(jī)制和在同一個(gè)tcp連接上重新建立一個(gè)新的http請(qǐng)求有關(guān),也與關(guān)閉一個(gè)http請(qǐng)求有關(guān),跨度比較大,看了也比較混亂。如果對(duì)http請(qǐng)求初始化過(guò)程還不是很清楚,可以參考前面的文章。下一篇文章將會(huì)對(duì)nginx的延遲關(guān)閉功能進(jìn)行分析。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
filebeat同時(shí)收集錯(cuò)誤日志與普通日志并存詳解
這篇文章主要為大家介紹了filebeat同時(shí)收集錯(cuò)誤日志與普通日志并存詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08
ubuntu16.04下徹底卸載nginx的相關(guān)命令
nginx是一款自由的、開(kāi)源的、高性能的HTTP服務(wù)器和反向代理服務(wù)器;這篇文章主要介紹了ubuntu16.04下徹底卸載nginx的相關(guān)命令,需要的朋友可以參考下2018-12-12
前端服務(wù)器部署Nginx?+docker?+?ubuntu的完整過(guò)程
Docker是一個(gè)開(kāi)源的容器化平臺(tái),可以讓你快速構(gòu)建、測(cè)試和部署應(yīng)用程序,Nginx是一個(gè)高性能的Web服務(wù)器和反向代理服務(wù)器,常用于部署靜態(tài)網(wǎng)站、負(fù)載均衡等場(chǎng)景,這篇文章主要介紹了前端服務(wù)器部署Nginx?+docker?+?ubuntu的完整過(guò)程,需要的朋友可以參考下2025-11-11
Nginx超時(shí)時(shí)間的配置說(shuō)明
Nginx超時(shí)時(shí)間非常重要,因?yàn)樗鼘⒅苯佑绊懢W(wǎng)站的響應(yīng)速度和用戶體驗(yàn),本文主要介紹了Nginx超時(shí)時(shí)間的配置說(shuō)明,具有一定的參考價(jià)值,感興趣的可以了解一下2024-07-07
Nginx try_files 指令常見(jiàn)用法示例
try_files是Nginx用于按順序檢查文件是否存在并返回第一個(gè)找到的文件,本文給大家介紹Nginx try_files 指令常見(jiàn)用法示例,感興趣的朋友跟隨小編一起看看吧2026-05-05

