摘要:如果第次探測(cè)包就收到響應(yīng)了則后次的不再發(fā)。對(duì)于一個(gè)已經(jīng)建立的連接。一直沒(méi)有收到應(yīng)答,則發(fā)送包關(guān)閉連接。若收到應(yīng)答,則將計(jì)時(shí)器清零。
默認(rèn)http1.1協(xié)議的請(qǐng)求頭是默認(rèn)開(kāi)啟keepalive,如圖:
那什么是keepalive?作用是什么?keepalive是在TCP中一個(gè)可以檢測(cè)死連接的機(jī)制,作用是保持socket長(zhǎng)連接不被斷開(kāi),屬于tcp層的功能,并不屬于應(yīng)用層。
TCP層怎么做到保持長(zhǎng)連接的呢?先看keepalive的用法:有三個(gè)參數(shù),開(kāi)放給應(yīng)用層使用
sk->keepalive_probes:探測(cè)次數(shù),重試次數(shù) sk->keepalive_time 探測(cè)的心跳間隔,TCP鏈接在多少秒之后沒(méi)有數(shù)據(jù)報(bào)文傳輸啟動(dòng)探測(cè)報(bào)文 sk->keepalive_intvl 探測(cè)間隔,未收到回復(fù)時(shí),重試的時(shí)間間隔
默認(rèn)配置查看:
[***@*** ~]$ cat /proc/sys/net/ipv4/tcp_keepalive_time 7200 [***@*** ~]$ cat /proc/sys/net/ipv4/tcp_keepalive_intvl 75 [***@*** ~]$ cat /proc/sys/net/ipv4/tcp_keepalive_probes 9
使用方法:
int keepalive = 1; // 開(kāi)啟keepalive屬性 int keepidle = 60; // 如該連接在60秒內(nèi)沒(méi)有任何數(shù)據(jù)往來(lái),則進(jìn)行探測(cè) int keepinterval = 5; // 探測(cè)時(shí)發(fā)包的時(shí)間間隔為5 秒 int keepcount = 3; // 探測(cè)嘗試的次數(shù)。如果第1次探測(cè)包就收到響應(yīng)了,則后2次的不再發(fā)。并且清零該計(jì)數(shù) setsockopt(rs, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepalive , sizeof(keepalive )); setsockopt(rs, SOL_TCP, TCP_KEEPIDLE, (void*)&keepidle , sizeof(keepidle )); setsockopt(rs, SOL_TCP, TCP_KEEPINTVL, (void *)&keepinterval , sizeof(keepinterval )); setsockopt(rs, SOL_TCP, TCP_KEEPCNT, (void *)&keepcount , sizeof(keepcount ));
應(yīng)用層這么設(shè)置后,會(huì)把默認(rèn)配置覆蓋,走手動(dòng)設(shè)置的配置。
對(duì)于一個(gè)已經(jīng)建立的tcp連接。如果在keepalive_time時(shí)間內(nèi)雙方?jīng)]有任何的數(shù)據(jù)包傳輸,則開(kāi)啟keepalive功能的一端將發(fā)送 keepalive數(shù)據(jù)心跳包,若沒(méi)有收到應(yīng)答,則每隔keepalive_intvl時(shí)間再發(fā)送該數(shù)據(jù)包,發(fā)送keepalive_probes次。一直沒(méi)有 收到應(yīng)答,則發(fā)送rst包關(guān)閉連接。若收到應(yīng)答,則將計(jì)時(shí)器清零。
根據(jù)抓包繼續(xù)分析keepalive發(fā)送及回復(fù)的心跳包內(nèi)容:
tcp頭部結(jié)構(gòu)體源碼為:
typedef struct _TCP_HEADER { short m_sSourPort; // 源端口號(hào)16bit short m_sDestPort; // 目的端口號(hào)16bit unsigned int m_uiSequNum; // req字段 序列號(hào)32bit unsigned int m_uiAcknowledgeNum; //ack字段 確認(rèn)號(hào)32bit short m_sHeaderLenAndFlag; // 前4位:TCP頭長(zhǎng)度;中6位:保留;后6位:標(biāo)志位 short m_sWindowSize; //win字段 窗口大小16bit short m_sCheckSum; // 檢驗(yàn)和16bit short m_surgentPointer; // 緊急數(shù)據(jù)偏移量16bit }__attribute__((packed))TCP_HEADER, *PTCP_HEADER;
看發(fā)送的心跳包內(nèi)容:
0000 d4 6d 50 f5 02 7f f4 5c 89 cb 35 29 08 00 //mac頭 14字節(jié): 45 00 // ip頭 20字節(jié) : 0010 00 28 10 f4 00 00 40 06 5b dd ac 19 42 76 0a b3 0020 14 bd e4 4a 1f 7c 32 7e 7a cb 4c bc 55 08 50 10 // tcp頭 20字節(jié) 0030 10 00 3f 00 00 00 //分析tcp頭部?jī)?nèi)容 e4 4a //源端口號(hào)16bit 10進(jìn)制為:58442 1f 7c //目的端口號(hào)16bit 10進(jìn)制為 : 8060 32 7e 7a cb // req字段 序列號(hào)32bit 10進(jìn)制為 : 4c bc 55 08 // ack字段 確認(rèn)號(hào)32bit 5 // 前4位:TCP頭長(zhǎng)度 5*4 =20 字節(jié) 沒(méi)問(wèn)題 0 10 /// 中6位:保留;后6位:標(biāo)志位 10 代表倒數(shù)第5位為1, 標(biāo)識(shí)改tcp包為 ACK 確認(rèn)包 0030 10 00 3f 00 00 00
繼續(xù)看回復(fù)的心跳包內(nèi)容 :
0000 f4 5c 89 cb 35 29 d4 6d 50 f5 02 7f 08 00 45 00 0010 00 34 47 28 40 00 36 06 ef 9c 0a b3 14 bd ac 19 0020 42 76 // 前面數(shù)據(jù)不解讀 1f 7c e4 4a 4c bc 55 08 32 7e 7a cc 8// TCP頭長(zhǎng)度為8 * 4 = 32 除了頭部 還有 選項(xiàng)數(shù)據(jù) 12字節(jié) 0 10 // 中6位:保留;后6位:標(biāo)志位 10 代表倒數(shù)第5位為1, 標(biāo)識(shí)該tcp包為 ACK 確認(rèn)包 0030 01 3f //win字段 窗口大小16bit 4e 0d // 檢驗(yàn)和16bit 00 00 // 緊急數(shù)據(jù)偏移量16bit 01 01 08 0a 00 59 be 1c 39 13 0040 cf 12 // 選項(xiàng)數(shù)據(jù) 12字節(jié)
由上可以看出,tcp維持長(zhǎng)連接的心跳包是由瀏覽器向服務(wù)器先出發(fā)送一個(gè)ACK包,然后服務(wù)器再回復(fù)一個(gè)ACK包,且?guī)Я诉x項(xiàng)數(shù)據(jù)
nginx會(huì)怎么處理keepalive請(qǐng)求,都會(huì)做哪些事情?首先做的是版本判斷 :http協(xié)議版本低于1.1時(shí),該鏈接的keepalive置為0 if (r->http_version < NGX_HTTP_VERSION_11) { r->keepalive = 0; } ngx_http_process_connection 函數(shù)中 ngx_http_request_t 中帶有keep-alive則把改鏈接標(biāo)識(shí)起來(lái) if (ngx_strcasestrn(h->value.data, "keep-alive", 10 - 1)) { r->headers_in.connection_type = NGX_HTTP_CONNECTION_KEEP_ALIVE; } ngx_http_handler函數(shù)中對(duì)r->headers_in.connection_type 判斷,給r->keepalive賦值為1 switch (r->headers_in.connection_type) { case NGX_HTTP_CONNECTION_KEEP_ALIVE: r->keepalive = 1; break; } ngx_configure_listening_sockets函數(shù)中,當(dāng)keepalive為1時(shí),對(duì)該連接開(kāi)啟KEEPALIVE,之后tcp底層就會(huì)對(duì)該連接fd做檢測(cè)死連接的機(jī)制,保持長(zhǎng)連接,不斷開(kāi)。 if (ls[i].keepalive) { value = (ls[i].keepalive == 1) ? 1 : 0; if (setsockopt(ls[i].fd, SOL_SOCKET, SO_KEEPALIVE,//開(kāi)啟keepalive功能 (const void *) &value, sizeof(int)) == -1) }nginx什么時(shí)候長(zhǎng)連接會(huì)斷開(kāi)呢?
在nginx通過(guò) setsockopt(ls[i].fd, SOL_SOCKET, SO_KEEPALIVE,(const void *) &value, sizeof(int))開(kāi)啟keepalive后,會(huì)始終和客戶端保持長(zhǎng)連接,如此會(huì)出現(xiàn)一個(gè)很嚴(yán)峻的問(wèn)題,
每個(gè)woker的能保持的連接數(shù)是有限的(ep = epoll_create(cycle->connection_n / 2); cycle->connection_n / 2 為epoll能管理的fd上限),如此一來(lái),連接數(shù)很快就被耗盡,這時(shí)候nginx應(yīng)該怎么處理 ?
為了找到這個(gè)答案,我們來(lái)看nginx關(guān)于keeoalive的兩個(gè)配置參數(shù)
keepalive_timeout
keepalive_timeout timeout [header_timeout];
第一個(gè)參數(shù):設(shè)置keep-alive客戶端連接在服務(wù)器端保持開(kāi)啟的超時(shí)值(默認(rèn)75s);值為0會(huì)禁用keep-alive客戶端連接;
第二個(gè)參數(shù):可選、在響應(yīng)的header域中設(shè)置一個(gè)值“Keep-Alive: timeout=time”;通??梢圆挥迷O(shè)置;
注:keepalive_timeout默認(rèn)75s
keepalive_requests
keepalive_requests指令用于設(shè)置一個(gè)keep-alive連接上可以服務(wù)的請(qǐng)求的最大數(shù)量,當(dāng)最大請(qǐng)求數(shù)量達(dá)到時(shí),連接被關(guān)閉,值為0會(huì)也禁用keep-alive客戶端連接;。默認(rèn)是100。
答案顯而易見(jiàn),通過(guò) keepalive_timeout keepalive_requests 來(lái)管理長(zhǎng)連接,
當(dāng)一個(gè)tcp連接存活時(shí)間超過(guò) keepalive_timeout 時(shí)則會(huì)被close掉,nginx的具體實(shí)現(xiàn),是通過(guò)定時(shí)器來(lái)做的
當(dāng)一個(gè)tcp連接最大情書(shū)數(shù)超過(guò) keepalive_requests 時(shí)則也會(huì)被close掉
通過(guò)這兩個(gè)機(jī)制來(lái)保證每個(gè)worker的連接數(shù)不會(huì)超過(guò)epoll所能管理的數(shù)目。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/40140.html
摘要:猜測(cè)原因是一端異常關(guān)閉了連接卻沒(méi)有通知對(duì)端,或者通知了對(duì)端但對(duì)端沒(méi)有收到。序號(hào)請(qǐng)求設(shè)置了超時(shí)時(shí)間為,因此發(fā)送包。之后繼續(xù)測(cè)試,沒(méi)有發(fā)現(xiàn)丟包。序號(hào)空閑分鐘后,主動(dòng)發(fā)起報(bào)文,關(guān)閉連接。 一、故障 showImg(https://segmentfault.com/img/bVbnJQk?w=1610&h=140); 基本架構(gòu)如圖所示,客戶端發(fā)起 http 請(qǐng)求給 nginx,nginx 轉(zhuǎn)發(fā)...
摘要:服務(wù)器市場(chǎng)份額。子進(jìn)程負(fù)責(zé)創(chuàng)建由指令設(shè)置的服務(wù)器線程,同時(shí)還負(fù)責(zé)監(jiān)聽(tīng)接收到的請(qǐng)求,并將請(qǐng)求分發(fā)給處理線程。在版本引入了模塊,這個(gè)模塊基于模塊創(chuàng)建的,并加入了獨(dú)立的監(jiān)聽(tīng)線程來(lái)管理請(qǐng)求處理完成后的休眠的連接?;谑录姆?wù)器完勝。 譯文首發(fā)于 Apache 與 Nginx 性能對(duì)比:Web 服務(wù)器優(yōu)化技術(shù),轉(zhuǎn)載請(qǐng)注明出處。 多年前 Apache 基金會(huì) Web 服務(wù)器 簡(jiǎn)稱「Apache」...
摘要:服務(wù)器市場(chǎng)份額。子進(jìn)程負(fù)責(zé)創(chuàng)建由指令設(shè)置的服務(wù)器線程,同時(shí)還負(fù)責(zé)監(jiān)聽(tīng)接收到的請(qǐng)求,并將請(qǐng)求分發(fā)給處理線程。在版本引入了模塊,這個(gè)模塊基于模塊創(chuàng)建的,并加入了獨(dú)立的監(jiān)聽(tīng)線程來(lái)管理請(qǐng)求處理完成后的休眠的連接?;谑录姆?wù)器完勝。 譯文首發(fā)于 Apache 與 Nginx 性能對(duì)比:Web 服務(wù)器優(yōu)化技術(shù),轉(zhuǎn)載請(qǐng)注明出處。 多年前 Apache 基金會(huì) Web 服務(wù)器 簡(jiǎn)稱「Apache」...
摘要:引入機(jī)制,定時(shí)給發(fā)送空數(shù)據(jù)包,稱為心跳包,一旦發(fā)現(xiàn)網(wǎng)絡(luò)不通則關(guān)閉連接。一般使用的方案是服務(wù)端根據(jù)客戶端請(qǐng)求頭的某些字段發(fā)送最合適的版本。是為了解決某些瀏覽器啟用帶來(lái)的問(wèn)題。首先生成用于分割不同字段。消息主體最后以結(jié)束。 基本命令start nginxnginx -s stop(立即停止)nginx -s quit(平緩?fù)V?,有?qǐng)求事不會(huì)意外停止)nginx -s reload遇到的坑:...
摘要:確定偵聽(tīng)通配符地址的套接字是否只接受連接,或者是接受和連接。此參數(shù)配置偵聽(tīng)套接字的行為。某些操作系統(tǒng)支持使用,和套接字選項(xiàng)在每個(gè)套接字上設(shè)置保持活動(dòng)參數(shù)??梢允÷砸粋€(gè)或兩個(gè)參數(shù),在這種情況下,相應(yīng)套接字選項(xiàng)的系統(tǒng)默認(rèn)設(shè)置將有效。 Nginx代理TCP主要是使用stream模塊,這個(gè)功能是從1.9.0版本開(kāi)始的。我用它來(lái)代理Mysql。 一、配置代碼 stream { upstr...
閱讀 2310·2023-04-26 00:01
閱讀 805·2021-10-27 14:13
閱讀 1835·2021-09-02 15:11
閱讀 3389·2019-08-29 12:52
閱讀 538·2019-08-26 12:00
閱讀 2572·2019-08-26 10:57
閱讀 3413·2019-08-26 10:32
閱讀 2855·2019-08-23 18:29