windows。forms.timer设置第一次不等待_面试官:换人!他连 TCP 这几个参数都不懂(一)...
前言
TCP 性能的提升不僅考察 TCP 的理論知識(shí),還考察了對(duì)于操作系統(tǒng)提供的內(nèi)核參數(shù)的理解與應(yīng)用。
TCP 協(xié)議是由操作系統(tǒng)實(shí)現(xiàn),所以操作系統(tǒng)提供了不少調(diào)節(jié) TCP 的參數(shù)。
如何正確有效的使用這些參數(shù),來提高 TCP 性能是一個(gè)不那么簡(jiǎn)單事情。我們需要針對(duì) TCP 每個(gè)階段的問題來對(duì)癥下藥,而不是病急亂投醫(yī)。
接下來,將以三個(gè)角度來闡述提升 TCP 的策略,分別是:
TCP 三次握手的性能提升;
TCP 四次揮手的性能提升;
TCP 數(shù)據(jù)傳輸?shù)男阅芴嵘?#xff1b;
01 TCP 三次握手的性能提升
TCP 是面向連接的、可靠的、雙向傳輸?shù)膫鬏攲油ㄐ艆f(xié)議,所以在傳輸數(shù)據(jù)之前需要經(jīng)過三次握手才能建立連接。
那么,三次握手的過程在一個(gè) HTTP 請(qǐng)求的平均時(shí)間占比 10% 以上,在網(wǎng)絡(luò)狀態(tài)不佳、高并發(fā)或者遭遇 SYN 攻擊等場(chǎng)景中,如果不能有效正確的調(diào)節(jié)三次握手中的參數(shù),就會(huì)對(duì)性能產(chǎn)生很多的影響。
如何正確有效的使用這些參數(shù),來提高 TCP 三次握手的性能,這就需要理解「三次握手的狀態(tài)變遷」,這樣當(dāng)出現(xiàn)問題時(shí),先用 netstat 命令查看是哪個(gè)握手階段出現(xiàn)了問題,再來對(duì)癥下藥,而不是病急亂投醫(yī)。
客戶端和服務(wù)端都可以針對(duì)三次握手優(yōu)化性能。主動(dòng)發(fā)起連接的客戶端優(yōu)化相對(duì)簡(jiǎn)單些,而服務(wù)端需要監(jiān)聽端口,屬于被動(dòng)連接方,其間保持許多的中間狀態(tài),優(yōu)化方法相對(duì)復(fù)雜一些。
所以,客戶端(主動(dòng)發(fā)起連接方)和服務(wù)端(被動(dòng)連接方)優(yōu)化的方式是不同的,接下來分別針對(duì)客戶端和服務(wù)端優(yōu)化。
只有同步了序列號(hào)才有可靠傳輸,TCP 許多特性都依賴于序列號(hào)實(shí)現(xiàn),比如流量控制、丟包重傳等,這也是三次握手中的報(bào)文稱為 SYN 的原因,SYN 的全稱就叫 Synchronize Sequence Numbers(同步序列號(hào))。
SYN_SENT 狀態(tài)的優(yōu)化
客戶端作為主動(dòng)發(fā)起連接方,首先它將發(fā)送 SYN 包,于是客戶端的連接就會(huì)處于?SYN_SENT?狀態(tài)。
客戶端在等待服務(wù)端回復(fù)的 ACK 報(bào)文,正常情況下,服務(wù)器會(huì)在幾毫秒內(nèi)返回 SYN+ACK ,但如果客戶端長(zhǎng)時(shí)間沒有收到 SYN+ACK 報(bào)文,則會(huì)重發(fā) SYN 包,重發(fā)的次數(shù)由?tcp_syn_retries?參數(shù)控制,默認(rèn)是 5 次:
通常,第一次超時(shí)重傳是在 1 秒后,第二次超時(shí)重傳是在 2 秒,第三次超時(shí)重傳是在 4 秒后,第四次超時(shí)重傳是在 8 秒后,第五次是在超時(shí)重傳 16 秒后。沒錯(cuò),每次超時(shí)的時(shí)間是上一次的 2 倍。
當(dāng)?shù)谖宕纬瑫r(shí)重傳后,會(huì)繼續(xù)等待 32 秒,如果仍然服務(wù)端沒有回應(yīng) ACK,客戶端就會(huì)終止三次握手。
所以,總耗時(shí)是 1+2+4+8+16+32=63 秒,大約 1 分鐘左右。
你可以根據(jù)網(wǎng)絡(luò)的穩(wěn)定性和目標(biāo)服務(wù)器的繁忙程度修改 SYN 的重傳次數(shù),調(diào)整客戶端的三次握手時(shí)間上限。比如內(nèi)網(wǎng)中通訊時(shí),就可以適當(dāng)調(diào)低重試次數(shù),盡快把錯(cuò)誤暴露給應(yīng)用程序。
服務(wù)端優(yōu)化
當(dāng)服務(wù)端收到 SYN 包后,服務(wù)端會(huì)立馬回復(fù) SYN+ACK 包,表明確認(rèn)收到了客戶端的序列號(hào),同時(shí)也把自己的序列號(hào)發(fā)給對(duì)方。
此時(shí),服務(wù)端出現(xiàn)了新連接,狀態(tài)是?SYN_RCV。在這個(gè)狀態(tài)下,Linux 內(nèi)核就會(huì)建立一個(gè)「半連接隊(duì)列」來維護(hù)「未完成」的握手信息,當(dāng)半連接隊(duì)列溢出后,服務(wù)端就無法再建立新的連接。
SYN 攻擊,攻擊的是就是這個(gè)半連接隊(duì)列。
如何查看由于 SYN 半連接隊(duì)列已滿,而被丟棄連接的情況?
我們可以通過該?netstat -s?命令給出的統(tǒng)計(jì)結(jié)果中, 可以得到由于半連接隊(duì)列已滿,引發(fā)的失敗次數(shù):
上面輸出的數(shù)值是累計(jì)值,表示共有多少個(gè) TCP 連接因?yàn)榘脒B接隊(duì)列溢出而被丟棄。隔幾秒執(zhí)行幾次,如果有上升的趨勢(shì),說明當(dāng)前存在半連接隊(duì)列溢出的現(xiàn)象。
如何調(diào)整 SYN 半連接隊(duì)列大小?
要想增大半連接隊(duì)列,不能只單純?cè)龃?tcp_max_syn_backlog 的值,還需一同增大 somaxconn 和 backlog,也就是增大 accept 隊(duì)列。否則,只單純?cè)龃?tcp_max_syn_backlog 是無效的。
增大 tcp_max_syn_backlog 和 somaxconn 的方法是修改 Linux 內(nèi)核參數(shù):
增大 backlog 的方式,每個(gè) Web 服務(wù)都不同,比如 Nginx 增大 backlog 的方法如下:
最后,改變了如上這些參數(shù)后,要重啟 Nginx 服務(wù),因?yàn)?SYN 半連接隊(duì)列和 accept 隊(duì)列都是在?listen()?初始化的。
如果 SYN 半連接隊(duì)列已滿,只能丟棄連接嗎?
并不是這樣,開啟 syncookies 功能就可以在不使用 SYN 半連接隊(duì)列的情況下成功建立連接。
syncookies 的工作原理:服務(wù)器根據(jù)當(dāng)前狀態(tài)計(jì)算出一個(gè)值,放在己方發(fā)出的 SYN+ACK 報(bào)文中發(fā)出,當(dāng)客戶端返回 ACK 報(bào)文時(shí),取出該值驗(yàn)證,如果合法,就認(rèn)為連接建立成功,如下圖所示。
syncookies 參數(shù)主要有以下三個(gè)值:
0 值,表示關(guān)閉該功能;
1 值,表示僅當(dāng) SYN 半連接隊(duì)列放不下時(shí),再啟用它;
2 值,表示無條件開啟功能;
那么在應(yīng)對(duì) SYN 攻擊時(shí),只需要設(shè)置為 1 即可:
SYN_RCV 狀態(tài)的優(yōu)化
當(dāng)客戶端接收到服務(wù)器發(fā)來的 SYN+ACK 報(bào)文后,就會(huì)回復(fù) ACK 給服務(wù)器,同時(shí)客戶端連接狀態(tài)從 SYN_SENT 轉(zhuǎn)換為 ESTABLISHED,表示連接建立成功。
服務(wù)器端連接成功建立的時(shí)間還要再往后,等到服務(wù)端收到客戶端的 ACK 后,服務(wù)端的連接狀態(tài)才變?yōu)?ESTABLISHED。
如果服務(wù)器沒有收到 ACK,就會(huì)重發(fā) SYN+ACK 報(bào)文,同時(shí)一直處于 SYN_RCV 狀態(tài)。
當(dāng)網(wǎng)絡(luò)繁忙、不穩(wěn)定時(shí),報(bào)文丟失就會(huì)變嚴(yán)重,此時(shí)應(yīng)該調(diào)大重發(fā)次數(shù)。反之則可以調(diào)小重發(fā)次數(shù)。修改重發(fā)次數(shù)的方法是,調(diào)整 tcp_synack_retries 參數(shù):
tcp_synack_retries 的默認(rèn)重試次數(shù)是 5 次,與客戶端重傳 SYN 類似,它的重傳會(huì)經(jīng)歷 1、2、4、8、16 秒,最后一次重傳后會(huì)繼續(xù)等待 32 秒,如果服務(wù)端仍然沒有收到 ACK,才會(huì)關(guān)閉連接,故共需要等待 63 秒。
服務(wù)器收到 ACK 后連接建立成功,此時(shí),內(nèi)核會(huì)把連接從半連接隊(duì)列移除,然后創(chuàng)建新的完全的連接,并將其添加到 accept 隊(duì)列,等待進(jìn)程調(diào)用 accept 函數(shù)時(shí)把連接取出來。
如果進(jìn)程不能及時(shí)地調(diào)用 accept 函數(shù),就會(huì)造成 accept 隊(duì)列(也稱全連接隊(duì)列)溢出,最終導(dǎo)致建立好的 TCP 連接被丟棄。
accept 隊(duì)列已滿,只能丟棄連接嗎?
丟棄連接只是 Linux 的默認(rèn)行為,我們還可以選擇向客戶端發(fā)送 RST 復(fù)位報(bào)文,告訴客戶端連接已經(jīng)建立失敗。打開這一功能需要將 tcp_abort_on_overflow 參數(shù)設(shè)置為 1。
tcp_abort_on_overflow 共有兩個(gè)值分別是 0 和 1,其分別表示:
0 :如果 accept 隊(duì)列滿了,那么 server 扔掉 client 發(fā)過來的 ack
1 :如果 accept 隊(duì)列滿了,server 發(fā)送一個(gè)?RST?包給 client,表示廢掉這個(gè)握手過程和這個(gè)連接
如果要想知道客戶端連接不上服務(wù)端,是不是服務(wù)端 TCP 全連接隊(duì)列滿的原因,那么可以把 tcp_abort_on_overflow 設(shè)置為 1,這時(shí)如果在客戶端異常中可以看到很多?connection reset by peer?的錯(cuò)誤,那么就可以證明是由于服務(wù)端 TCP 全連接隊(duì)列溢出的問題。
通常情況下,應(yīng)當(dāng)把 tcp_abort_on_overflow 設(shè)置為 0,因?yàn)檫@樣更有利于應(yīng)對(duì)突發(fā)流量。
舉個(gè)例子,當(dāng) accept 隊(duì)列滿導(dǎo)致服務(wù)器丟掉了 ACK,與此同時(shí),客戶端的連接狀態(tài)卻是 ESTABLISHED,客戶端進(jìn)程就在建立好的連接上發(fā)送請(qǐng)求。只要服務(wù)器沒有為請(qǐng)求回復(fù) ACK,客戶端的請(qǐng)求就會(huì)被多次「重發(fā)」。如果服務(wù)器上的進(jìn)程只是短暫的繁忙造成 accept 隊(duì)列滿,那么當(dāng) accept 隊(duì)列有空位時(shí),再次接收到的請(qǐng)求報(bào)文由于含有 ACK,仍然會(huì)觸發(fā)服務(wù)器端成功建立連接。
所以,tcp_abort_on_overflow 設(shè)為 0 可以提高連接建立的成功率,只有你非常肯定 TCP 全連接隊(duì)列會(huì)長(zhǎng)期溢出時(shí),才能設(shè)置為 1 以盡快通知客戶端。
如何調(diào)整 accept 隊(duì)列的長(zhǎng)度呢?
accept 隊(duì)列的長(zhǎng)度取決于 somaxconn 和 backlog 之間的最小值,也就是 min (somaxconn, backlog),其中:
somaxconn 是 Linux 內(nèi)核的參數(shù),默認(rèn)值是 128,可以通過?net.core.somaxconn?來設(shè)置其值;
backlog 是?listen(int sockfd, int backlog)?函數(shù)中的 backlog 大小;
Tomcat、Nginx、Apache 常見的 Web 服務(wù)的 backlog 默認(rèn)值都是 511
如何查看服務(wù)端進(jìn)程 accept 隊(duì)列的長(zhǎng)度?
可以通過?ss -ltn?命令查看:
如何查看由于 accept 連接隊(duì)列已滿,而被丟棄的連接?
當(dāng)超過了 accept 連接隊(duì)列,服務(wù)端則會(huì)丟掉后續(xù)進(jìn)來的 TCP 連接,丟掉的 TCP 連接的個(gè)數(shù)會(huì)被統(tǒng)計(jì)起來,我們可以使用?netstat -s?命令來查看:
上面看到的 41150 times ,表示 accept 隊(duì)列溢出的次數(shù),注意這個(gè)是累計(jì)值。可以隔幾秒鐘執(zhí)行下,如果這個(gè)數(shù)字一直在增加的話,說明 accept 連接隊(duì)列偶爾滿了。
如果持續(xù)不斷地有連接因?yàn)?accept 隊(duì)列溢出被丟棄,就應(yīng)該調(diào)大 backlog 以及 somaxconn 參數(shù)。
如何繞過三次握手?
以上我們只是在對(duì)三次握手的過程進(jìn)行優(yōu)化,接下來我們看看如何繞過三次握手發(fā)送數(shù)據(jù)。
三次握手建立連接造成的后果就是,HTTP 請(qǐng)求必須在一個(gè) RTT(從客戶端到服務(wù)器一個(gè)往返的時(shí)間)后才能發(fā)送。
在 Linux 3.7 內(nèi)核版本之后,提供了 TCP Fast Open 功能,這個(gè)功能可以減少 TCP 連接建立的時(shí)延。
接下來說說,TCP Fast Open 功能的工作方式。
在客戶端首次建立連接時(shí)的過程:
客戶端發(fā)送 SYN 報(bào)文,該報(bào)文包含 Fast Open 選項(xiàng),且該選項(xiàng)的 Cookie 為空,這表明客戶端請(qǐng)求 Fast Open Cookie;
支持 TCP Fast Open 的服務(wù)器生成 Cookie,并將其置于 SYN-ACK 數(shù)據(jù)包中的 Fast Open 選項(xiàng)以發(fā)回客戶端;
客戶端收到 SYN-ACK 后,本地緩存 Fast Open 選項(xiàng)中的 Cookie。
所以,第一次發(fā)起 HTTP GET 請(qǐng)求的時(shí)候,還是需要正常的三次握手流程。
之后,如果客戶端再次向服務(wù)器建立連接時(shí)的過程:
客戶端發(fā)送 SYN 報(bào)文,該報(bào)文包含「數(shù)據(jù)」(對(duì)于非 TFO 的普通 TCP 握手過程,SYN 報(bào)文中不包含「數(shù)據(jù)」)以及此前記錄的 Cookie;
支持 TCP Fast Open 的服務(wù)器會(huì)對(duì)收到 Cookie 進(jìn)行校驗(yàn):如果 Cookie 有效,服務(wù)器將在 SYN-ACK 報(bào)文中對(duì) SYN 和「數(shù)據(jù)」進(jìn)行確認(rèn),服務(wù)器隨后將「數(shù)據(jù)」遞送至相應(yīng)的應(yīng)用程序;如果 Cookie 無效,服務(wù)器將丟棄 SYN 報(bào)文中包含的「數(shù)據(jù)」,且其隨后發(fā)出的 SYN-ACK 報(bào)文將只確認(rèn) SYN 的對(duì)應(yīng)序列號(hào);
如果服務(wù)器接受了 SYN 報(bào)文中的「數(shù)據(jù)」,服務(wù)器可在握手完成之前發(fā)送「數(shù)據(jù)」,這就減少了握手帶來的 1 個(gè) RTT 的時(shí)間消耗;
客戶端將發(fā)送 ACK 確認(rèn)服務(wù)器發(fā)回的 SYN 以及「數(shù)據(jù)」,但如果客戶端在初始的 SYN 報(bào)文中發(fā)送的「數(shù)據(jù)」沒有被確認(rèn),則客戶端將重新發(fā)送「數(shù)據(jù)」;
此后的 TCP 連接的數(shù)據(jù)傳輸過程和非 TFO 的正常情況一致。
所以,之后發(fā)起 HTTP GET 請(qǐng)求的時(shí)候,可以繞過三次握手,這就減少了握手帶來的 1 個(gè) RTT 的時(shí)間消耗。
注:客戶端在請(qǐng)求并存儲(chǔ)了 Fast Open Cookie 之后,可以不斷重復(fù) TCP Fast Open 直至服務(wù)器認(rèn)為 Cookie 無效(通常為過期)。
Linux 下怎么打開 TCP Fast Open 功能呢?
在 Linux 系統(tǒng)中,可以通過設(shè)置 tcp_fastopn 內(nèi)核參數(shù),來打開 Fast Open 功能
tcp_fastopn 各個(gè)值的意義:
0 關(guān)閉
1 作為客戶端使用 Fast Open 功能
2 作為服務(wù)端使用 Fast Open 功能
3 無論作為客戶端還是服務(wù)器,都可以使用 Fast Open 功能
TCP Fast Open 功能需要客戶端和服務(wù)端同時(shí)支持,才有效果。
小結(jié)
本小結(jié)主要介紹了關(guān)于優(yōu)化 TCP 三次握手的幾個(gè) TCP 參數(shù)。
客戶端的優(yōu)化
當(dāng)客戶端發(fā)起 SYN 包時(shí),可以通過?tcp_syn_retries?控制其重傳的次數(shù)。
服務(wù)端的優(yōu)化
當(dāng)服務(wù)端 SYN 半連接隊(duì)列溢出后,會(huì)導(dǎo)致后續(xù)連接被丟棄,可以通過?netstat -s?觀察半連接隊(duì)列溢出的情況,如果 SYN 半連接隊(duì)列溢出情況比較嚴(yán)重,可以通過?tcp_max_syn_backlog、somaxconn、backlog?參數(shù)來調(diào)整 SYN 半連接隊(duì)列的大小。
服務(wù)端回復(fù) SYN+ACK 的重傳次數(shù)由?tcp_synack_retries?參數(shù)控制。如果遭受 SYN 攻擊,應(yīng)把?tcp_syncookies?參數(shù)設(shè)置為 1,表示僅在 SYN 隊(duì)列滿后開啟 syncookie 功能,可以保證正常的連接成功建立。
服務(wù)端收到客戶端返回的 ACK,會(huì)把連接移入 accpet 隊(duì)列,等待進(jìn)行調(diào)用 accpet () 函數(shù)取出連接。
可以通過?ss -lnt?查看服務(wù)端進(jìn)程的 accept 隊(duì)列長(zhǎng)度,如果 accept 隊(duì)列溢出,系統(tǒng)默認(rèn)丟棄 ACK,如果可以把?tcp_abort_on_overflow?設(shè)置為 1 ,表示用 RST 通知客戶端連接建立失敗。
如果 accpet 隊(duì)列溢出嚴(yán)重,可以通過 listen 函數(shù)的?backlog?參數(shù)和?somaxconn?系統(tǒng)參數(shù)提高隊(duì)列大小,accept 隊(duì)列長(zhǎng)度取決于 min (backlog, somaxconn)。
繞過三次握手
TCP Fast Open 功能可以繞過三次握手,使得 HTTP 請(qǐng)求減少了 1 個(gè) RTT 的時(shí)間,Linux 下可以通過?tcp_fastopen開啟該功能,同時(shí)必須保證服務(wù)端和客戶端同時(shí)支持。
總結(jié)
以上是生活随笔為你收集整理的windows。forms.timer设置第一次不等待_面试官:换人!他连 TCP 这几个参数都不懂(一)...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java什么是类和对象,Java类和对象
- 下一篇: 桥梁在线计算机监测系统,桥梁在线监测系统