直击案发现场!TCP 10倍延迟的真相是?
阿里妹導(dǎo)讀:什么是經(jīng)驗(yàn)?就是遇到問(wèn)題,解決問(wèn)題,總結(jié)方法。遇到的問(wèn)題多了,解決的辦法多了,經(jīng)驗(yàn)自然就積累出來(lái)了。今天的文章是阿里技術(shù)專家蟄劍在工作中遇到的一個(gè)問(wèn)題引發(fā)的對(duì)TCP性能和發(fā)送接收Buffer關(guān)系的系列思考(問(wèn)題:應(yīng)用通過(guò)專線從公司訪問(wèn)阿里云上的服務(wù),專線100M,時(shí)延20ms,一個(gè)SQL查詢了22M數(shù)據(jù)出現(xiàn)10倍+的信息延遲,不正常。)希望,你也能從中得到啟發(fā)。
前言
本文希望解析清楚,當(dāng)我們?cè)诖a中寫(xiě)下 socket.setSendBufferSize 和 sysctl 看到的rmem/wmem系統(tǒng)參數(shù)以及最終我們?cè)赥CP常常談到的接收發(fā)送窗口的關(guān)系,以及他們?cè)鯓佑绊慣CP傳輸?shù)男阅堋?/p>
先明確一下:文章標(biāo)題中所說(shuō)的Buffer指的是sysctl中的 rmem或者wmem,如果是代碼中指定的話對(duì)應(yīng)著SO_SNDBUF或者SO_RCVBUF,從TCP的概念來(lái)看對(duì)應(yīng)著發(fā)送窗口或者接收窗口。
TCP性能和發(fā)送接收Buffer的關(guān)系
相關(guān)參數(shù):
$sudo sysctl -a | egrep "rmem|wmem|adv_win|moderate" net.core.rmem_default = 212992 net.core.rmem_max = 212992 net.core.wmem_default = 212992 net.core.wmem_max = 212992 net.ipv4.tcp_adv_win_scale = 1 net.ipv4.tcp_moderate_rcvbuf = 1 net.ipv4.tcp_rmem = 4096 87380 6291456 net.ipv4.tcp_wmem = 4096 16384 4194304 net.ipv4.udp_rmem_min = 4096 net.ipv4.udp_wmem_min = 4096 vm.lowmem_reserve_ratio = 256 256 32先從碰到的一個(gè)問(wèn)題看起:
應(yīng)用通過(guò)專線從公司訪問(wèn)阿里云上的服務(wù),專線100M,時(shí)延20ms,一個(gè)SQL查詢了22M數(shù)據(jù),結(jié)果花了大概25秒,這太慢了,不正常。如果通過(guò)云上client訪問(wèn)云上服務(wù)那么1-2秒就返回了(說(shuō)明不跨網(wǎng)絡(luò)服務(wù)是正常的)。如果通過(guò)http或者scp從公司向云上傳輸這22M的數(shù)據(jù)大概兩秒鐘也傳送完畢了(說(shuō)明網(wǎng)絡(luò)帶寬不是瓶頸),所以這里問(wèn)題的原因基本上是我們的服務(wù)在這種網(wǎng)絡(luò)條件下有性能問(wèn)題,需要找出為什么。
抓包 tcpdump+wireshark
這個(gè)查詢結(jié)果22M的需要25秒,如下圖(wireshark 時(shí)序圖),橫軸是時(shí)間,縱軸是sequence number:
粗一看沒(méi)啥問(wèn)題,因?yàn)闀r(shí)間太長(zhǎng)掩蓋了問(wèn)題。把這個(gè)圖形放大,就看中間50ms內(nèi)的傳輸情況(橫軸是時(shí)間,縱軸是sequence number,一個(gè)點(diǎn)代表一個(gè)包)。
換個(gè)角度,看看窗口尺寸圖形:
從bytes in flight也大致能算出來(lái)總的傳輸時(shí)間 16K*1000/20=800Kb/秒
我們的應(yīng)用會(huì)默認(rèn)設(shè)置 socketSendBuffer 為16K:
socket.setSendBufferSize(16*1024) //16K send buffer
來(lái)看一下tcp包發(fā)送流程:
如果sendbuffer不夠就會(huì)卡在上圖中的第一步 sk_stream_wait_memory,通過(guò)systemtap腳本可以驗(yàn)證:
#!/usr/bin/stap # Simple probe to detect when a process is waiting for more socket send # buffer memory. Usually means the process is doing writes larger than the # socket send buffer size or there is a slow receiver at the other side. # Increasing the socket's send buffer size might help decrease application # latencies, but it might also make it worse, so buyer beware.# Typical output: timestamp in microseconds: procname(pid) event # # 1218230114875167: python(17631) blocked on full send buffer # 1218230114876196: python(17631) recovered from full send buffer # 1218230114876271: python(17631) blocked on full send buffer # 1218230114876479: python(17631) recovered from full send buffer probe kernel.function("sk_stream_wait_memory") {printf("%u: %s(%d) blocked on full send buffern",gettimeofday_us(), execname(), pid()) }probe kernel.function("sk_stream_wait_memory").return {printf("%u: %s(%d) recovered from full send buffern",gettimeofday_us(), execname(), pid()) }原理解析
如果tcp發(fā)送buffer也就是SO_SNDBUF只有16K的話,這些包很快都發(fā)出去了,但是這16K不能立即釋放出來(lái)填新的內(nèi)容進(jìn)去,因?yàn)閠cp要保證可靠,萬(wàn)一中間丟包了呢。只有等到這16K中的某些包ack了,才會(huì)填充一些新包進(jìn)來(lái)然后繼續(xù)發(fā)出去。由于這里rt基本是20ms,也就是16K發(fā)送完畢后,等了20ms才收到一些ack,這20ms應(yīng)用、內(nèi)核什么都不能做,所以就是如第二個(gè)圖中的大概20ms的等待平臺(tái)。
sendbuffer相當(dāng)于發(fā)送倉(cāng)庫(kù)的大小,倉(cāng)庫(kù)的貨物都發(fā)走后,不能立即騰出來(lái)發(fā)新的貨物,而是要等對(duì)方確認(rèn)收到了(ack)才能騰出來(lái)發(fā)新的貨物。 傳輸速度取決于發(fā)送倉(cāng)庫(kù)(sendbuffer)、接收倉(cāng)庫(kù)(recvbuffer)、路寬(帶寬)的大小,如果發(fā)送倉(cāng)庫(kù)(sendbuffer)足夠大了之后接下來(lái)的瓶頸就是高速公路了(帶寬、擁塞窗口)。
如果是UDP,就沒(méi)有可靠的概念,有數(shù)據(jù)統(tǒng)統(tǒng)發(fā)出去,根本不關(guān)心對(duì)方是否收到,也就不需要ack和這個(gè)發(fā)送buffer了。
幾個(gè)發(fā)送buffer相關(guān)的內(nèi)核參數(shù)
vm.lowmem_reserve_ratio = 256 256 32 net.core.wmem_max = 1048576 net.core.wmem_default = 124928 net.ipv4.tcp_wmem = 4096 16384 4194304 net.ipv4.udp_wmem_min = 4096net.ipv4.tcp_wmem 默認(rèn)就是16K,而且是能夠動(dòng)態(tài)調(diào)整的,只不過(guò)我們代碼中這塊的參數(shù)是很多年前從Cobra中繼承過(guò)來(lái)的,初始指定了sendbuffer的大小。代碼中設(shè)置了這個(gè)參數(shù)后就關(guān)閉了內(nèi)核的動(dòng)態(tài)調(diào)整功能,但是能看到http或者scp都很快,因?yàn)樗麄兊膕end buffer是動(dòng)態(tài)調(diào)整的,所以很快。
接收buffer是有開(kāi)關(guān)可以動(dòng)態(tài)控制的,發(fā)送buffer沒(méi)有開(kāi)關(guān)默認(rèn)就是開(kāi)啟,關(guān)閉只能在代碼層面來(lái)控制:
net.ipv4.tcp_moderate_rcvbuf
優(yōu)化
調(diào)整 socketSendBuffer 到256K,查詢時(shí)間從25秒下降到了4秒多,但是比理論帶寬所需要的時(shí)間略高。
繼續(xù)查看系統(tǒng) net.core.wmem_max 參數(shù)默認(rèn)最大是130K,所以即使我們代碼中設(shè)置256K實(shí)際使用的也是130K,調(diào)大這個(gè)系統(tǒng)參數(shù)后整個(gè)網(wǎng)絡(luò)傳輸時(shí)間大概2秒(跟100M帶寬匹配了,scp傳輸22M數(shù)據(jù)也要2秒),整體查詢時(shí)間2.8秒。測(cè)試用的mysql client短連接,如果代碼中的是長(zhǎng)連接的話會(huì)塊300-400ms(消掉了慢啟動(dòng)階段),這基本上是理論上最快速度了。
如果指定了tcp_wmem,則net.core.wmem_default被tcp_wmem的覆蓋。send Buffer在tcp_wmem的最小值和最大值之間自動(dòng)調(diào)整。如果調(diào)用setsockopt()設(shè)置了socket選項(xiàng)SO_SNDBUF,將關(guān)閉發(fā)送端緩沖的自動(dòng)調(diào)節(jié)機(jī)制,tcp_wmem將被忽略,SO_SNDBUF的最大值由net.core.wmem_max限制。
BDP 帶寬時(shí)延積
BDP=rtt*(帶寬/8)
這個(gè) buffer 調(diào)到1M測(cè)試沒(méi)有幫助,從理論計(jì)算BDP(帶寬時(shí)延積) 0.02秒*(100MB/8)=250Kb 所以SO_SNDBUF為256Kb的時(shí)候基本能跑滿帶寬了,再大實(shí)際意義也不大了。也就是前面所說(shuō)的倉(cāng)庫(kù)足夠后瓶頸在帶寬上了。
因?yàn)锽DP是250K,也就是擁塞窗口(帶寬、接收窗口和rt決定的)即將成為新的瓶頸,所以調(diào)大buffer沒(méi)意義了。
用tc構(gòu)造延時(shí)和帶寬限制的模擬重現(xiàn)環(huán)境
sudo tc qdisc del dev eth0 root netem delay 20ms sudo tc qdisc add dev eth0 root tbf rate 500kbit latency 50ms burst 15kb這個(gè)案例關(guān)于wmem的結(jié)論
默認(rèn)情況下Linux系統(tǒng)會(huì)自動(dòng)調(diào)整這個(gè)buffer(net.ipv4.tcp_wmem), 也就是不推薦程序中主動(dòng)去設(shè)置SO_SNDBUF,除非明確知道設(shè)置的值是最優(yōu)的。
從這里我們可以看到,有些理論知識(shí)點(diǎn)雖然我們知道,但是在實(shí)踐中很難聯(lián)系起來(lái),也就是常說(shuō)的無(wú)法學(xué)以致用,最開(kāi)始看到抓包結(jié)果的時(shí)候比較懷疑發(fā)送、接收窗口之類的,沒(méi)有直接想到send buffer上,理論跟實(shí)踐的鴻溝。
說(shuō)完發(fā)送Buffer(wmem)接下來(lái)我們接著一看看接收buffer(rmem)和接收窗口的情況
用這樣一個(gè)案例下來(lái)驗(yàn)證接收窗口的作用:
有一個(gè)batch insert語(yǔ)句,整個(gè)一次要插入5532條記錄,所有記錄大小總共是376K。
SO_RCVBUF很小的時(shí)候并且rtt很大對(duì)性能的影響
如果rtt是40ms,總共需要5-6秒鐘:
基本可以看到server一旦空出來(lái)點(diǎn)窗口,client馬上就發(fā)送數(shù)據(jù),由于這點(diǎn)窗口太小,rtt是40ms,也就是一個(gè)rtt才能傳3456字節(jié)的數(shù)據(jù),整個(gè)帶寬才80-90K,完全沒(méi)跑滿。
比較明顯間隔 40ms 一個(gè)等待臺(tái)階,臺(tái)階之間兩個(gè)包大概3K數(shù)據(jù),總的傳輸效率如下:
斜線越陡表示速度越快,從上圖看整體SQL上傳花了5.5秒,執(zhí)行0.5秒。
此時(shí)對(duì)應(yīng)的窗口尺寸:
窗口由最開(kāi)始28K(20個(gè)1448)很快降到了不到4K的樣子,然后基本游走在即將滿的邊緣,雖然讀取慢,幸好rtt也大,導(dǎo)致最終也沒(méi)有滿。(這個(gè)是3.1的Linux,應(yīng)用SO_RCVBUF設(shè)置的是8K,用一半來(lái)做接收窗口)。
SO_RCVBUF很小的時(shí)候并且rtt很小時(shí)對(duì)性能的影響
如果同樣的語(yǔ)句在 rtt 是0.1ms的話:
雖然明顯看到接收窗口經(jīng)常跑滿,但是因?yàn)閞tt很小,一旦窗口空出來(lái)很快就通知到對(duì)方了,所以整個(gè)過(guò)小的接收窗口也沒(méi)怎么影響到整體性能。
如上圖11.4秒整個(gè)SQL開(kāi)始,到11.41秒SQL上傳完畢,11.89秒執(zhí)行完畢(執(zhí)行花了0.5秒),上傳只花了0.01秒,接收窗口情況:
如圖,接收窗口由最開(kāi)始的28K降下來(lái),然后一直在5880和滿了之間跳動(dòng):
從這里可以得出結(jié)論,接收窗口的大小對(duì)性能的影響,rtt越大影響越明顯,當(dāng)然這里還需要應(yīng)用程序配合,如果應(yīng)用程序一直不讀走數(shù)據(jù)即使接收窗口再大也會(huì)堆滿的。
SO_RCVBUF和tcp window full的壞case
上圖中紅色平臺(tái)部分,停頓了大概6秒鐘沒(méi)有發(fā)任何有內(nèi)容的數(shù)據(jù)包,這6秒鐘具體在做什么如下圖所示,可以看到這個(gè)時(shí)候接收方的TCP Window Full,同時(shí)也能看到接收方(3306端口)的TCP Window Size是8192(8K),發(fā)送方(27545端口)是20480。
這個(gè)狀況跟前面描述的recv buffer太小不一樣,8K是很小,但是因?yàn)閞tt也很小,所以server總是能很快就ack收到了,接收窗口也一直不容易達(dá)到full狀態(tài),但是一旦接收窗口達(dá)到了full狀態(tài),居然需要驚人的6秒鐘才能恢復(fù),這等待的時(shí)間有點(diǎn)太長(zhǎng)了。這里應(yīng)該是應(yīng)用讀取數(shù)據(jù)太慢導(dǎo)致了耗時(shí)6秒才恢復(fù),所以最終這個(gè)請(qǐng)求執(zhí)行會(huì)非常非常慢(時(shí)間主要耗在了上傳SQL而不是執(zhí)行SQL)。
實(shí)際原因不知道,從讀取TCP數(shù)據(jù)的邏輯來(lái)看這里沒(méi)有明顯的block,可能的原因:
- request的SQL太大,Server(3306端口上的服務(wù))從TCP讀取SQL需要放到一塊分配好的內(nèi)存,內(nèi)存不夠的時(shí)候需要擴(kuò)容,擴(kuò)容有可能觸發(fā)fgc,從圖形來(lái)看,第一次滿就卡頓了,而且每次滿都卡頓,不像是這個(gè)原因
- request請(qǐng)求一次發(fā)過(guò)來(lái)的是多個(gè)SQL,應(yīng)用讀取SQL后,將SQL分成多個(gè),然后先執(zhí)行第一個(gè),第一個(gè)執(zhí)行完后返回response,再讀取第二個(gè)。圖形中卡頓前沒(méi)有response返回,所以也不是這個(gè)原因。
- ……其它未知原因
接收方不讀取數(shù)據(jù)導(dǎo)致的接收窗口滿同時(shí)有丟包發(fā)生
服務(wù)端返回?cái)?shù)據(jù)到client端,TCP協(xié)議棧ack這些包,但是應(yīng)用層沒(méi)讀走包,這個(gè)時(shí)候 SO_RCVBUF 堆積滿,client的TCP協(xié)議棧發(fā)送 ZeroWindow 標(biāo)志給服務(wù)端。也就是接收端的 buffer 堆滿了(但是服務(wù)端這個(gè)時(shí)候看到的bytes in fly是0,因?yàn)槎糰ck了),這時(shí)服務(wù)端不能繼續(xù)發(fā)數(shù)據(jù),要等 ZeroWindow 恢復(fù)。
那么接收端上層應(yīng)用不讀走包可能的原因:
- 應(yīng)用代碼卡頓、GC等等
應(yīng)用代碼邏輯上在做其它事情(比如Server將SQL分片到多個(gè)DB上,Server先讀取第一個(gè)分片,如果第一個(gè)分片數(shù)據(jù)很大很大,處理也慢,那么第二個(gè)分片數(shù)據(jù)都返回到了TCP buffer,也沒(méi)去讀取其它分片的結(jié)果集,直到第一個(gè)分片讀取完畢。如果SQL帶排序,那么Server。
- 會(huì)輪詢讀取多個(gè)分片,造成這種卡頓的概率小了很多
上圖這個(gè)流因?yàn)閼?yīng)用層不讀取TCP數(shù)據(jù),導(dǎo)致TCP接收Buffer滿,進(jìn)而接收窗口為0,server端不能再發(fā)送數(shù)據(jù)而卡住,但是ZeroWindow的探測(cè)包,client都有正常回復(fù),所以1903秒之后接收方窗口不為0后(window update)傳輸恢復(fù)。
這個(gè)截圖和前一個(gè)類似,是在Server上(3003端口)抓到的包,不同的是接收窗口為0后,server端多次探測(cè)(Server上抓包能看到),但是client端沒(méi)有回復(fù) ZeroWindow(也有可能是回復(fù)了,但是中間環(huán)節(jié)把a(bǔ)ck包丟了,或者這個(gè)探測(cè)包c(diǎn)lient沒(méi)收到),造成server端認(rèn)為client死了、不可達(dá)之類,進(jìn)而反復(fù)重傳,重傳超過(guò)15次之后,server端認(rèn)為這個(gè)連接死了,粗暴單方面斷開(kāi)(沒(méi)有reset和fin,因?yàn)闆](méi)必要,server認(rèn)為網(wǎng)絡(luò)連通性出了問(wèn)題)。
等到1800秒后,client的接收窗口恢復(fù)了,發(fā)個(gè)window update給server,這個(gè)時(shí)候server認(rèn)為這個(gè)連接已經(jīng)斷開(kāi)了,只能回復(fù)reset。
網(wǎng)絡(luò)不通,重傳超過(guò)一定的時(shí)間(tcp_retries2)然后斷開(kāi)這個(gè)連接是正常的,這里的問(wèn)題是:
為什么這種場(chǎng)景下丟包了,而且是針對(duì)某個(gè)stream一直丟包?
可能是因?yàn)檫@種場(chǎng)景下觸發(fā)了中間環(huán)節(jié)的流量管控,故意丟包了(比如proxy、slb、交換機(jī)都有可能做這種選擇性的丟包)
這里server認(rèn)為連接斷開(kāi),沒(méi)有發(fā)reset和fin,因?yàn)闆](méi)必要,server認(rèn)為網(wǎng)絡(luò)連通性出了問(wèn)題。client還不知道server上這個(gè)連接清理掉了,等client回復(fù)了一個(gè)window update,server早就認(rèn)為這個(gè)連接早斷了,突然收到一個(gè)update,莫名其妙,只能reset。
接收窗口和SO_RCVBUF的關(guān)系
初始接收窗口一般是 mss乘以初始cwnd(為了和慢啟動(dòng)邏輯兼容,不想一下子沖擊到網(wǎng)絡(luò)),如果沒(méi)有設(shè)置SO_RCVBUF,那么會(huì)根據(jù) net.ipv4.tcp_rmem 動(dòng)態(tài)變化,如果設(shè)置了SO_RCVBUF,那么接收窗口要向下面描述的值靠攏。
初始cwnd可以大致通過(guò)查看到:
ss -itmpn dst "10.81.212.8" State Recv-Q Send-Q Local Address:Port Peer Address:Port ESTAB 0 0 10.xx.xx.xxx:22 10.yy.yy.yyy:12345 users:(("sshd",pid=1442,fd=3))skmem:(r0,rb369280,t0,tb87040,f4096,w0,o0,bl0,d92)Here we can see this socket has Receive Buffer 369280 bytes, and Transmit Buffer 87040 bytes. Keep in mind the kernel will double any socket buffer allocation for overhead. So a process asks for 256 KiB buffer with setsockopt(SO_RCVBUF) then it will get 512 KiB buffer space. This is described on man 7 tcp.初始窗口計(jì)算的代碼邏輯,重點(diǎn)在18行:
/* TCP initial congestion window as per rfc6928 */ #define TCP_INIT_CWND 10/* 3. Try to fixup all. It is made immediately after connection enters* established state.*/ void tcp_init_buffer_space(struct sock *sk) {int tcp_app_win = sock_net(sk)->ipv4.sysctl_tcp_app_win;struct tcp_sock *tp = tcp_sk(sk);int maxwin;if (!(sk->sk_userlocks & SOCK_SNDBUF_LOCK))tcp_sndbuf_expand(sk);//初始最大接收窗口計(jì)算過(guò)程tp->rcvq_space.space = min_t(u32, tp->rcv_wnd, TCP_INIT_CWND * tp->advmss);tcp_mstamp_refresh(tp);tp->rcvq_space.time = tp->tcp_mstamp;tp->rcvq_space.seq = tp->copied_seq;maxwin = tcp_full_space(sk);if (tp->window_clamp >= maxwin) {tp->window_clamp = maxwin;if (tcp_app_win && maxwin > 4 * tp->advmss)tp->window_clamp = max(maxwin -(maxwin >> tcp_app_win),4 * tp->advmss);}/* Force reservation of one segment. */if (tcp_app_win &&tp->window_clamp > 2 * tp->advmss &&tp->window_clamp + tp->advmss > maxwin)tp->window_clamp = max(2 * tp->advmss, maxwin - tp->advmss);tp->rcv_ssthresh = min(tp->rcv_ssthresh, tp->window_clamp);tp->snd_cwnd_stamp = tcp_jiffies32; }傳輸過(guò)程中,最大接收窗口會(huì)動(dòng)態(tài)調(diào)整,當(dāng)指定了SO_RCVBUF后,實(shí)際buffer是兩倍SO_RCVBUF,但是要分出一部分(2^net.ipv4.tcp_adv_win_scale)來(lái)作為亂序報(bào)文緩存。
1.net.ipv4.tcp_adv_win_scale = 2 //2.6內(nèi)核,3.1中這個(gè)值默認(rèn)是1。
如果SO_RCVBUF是8K,總共就是16K,然后分出2^2分之一,也就是4分之一,還剩12K當(dāng)做接收窗口;如果設(shè)置的32K,那么接收窗口是48K。
static inline int tcp_win_from_space(const struct sock *sk, int space) {//space 傳入的時(shí)候就已經(jīng)是 2*SO_RCVBUF了int tcp_adv_win_scale = sock_net(sk)->ipv4.sysctl_tcp_adv_win_scale;return tcp_adv_win_scale <= 0 ?(space>>(-tcp_adv_win_scale)) :space - (space>>tcp_adv_win_scale); //sysctl參數(shù)tcp_adv_win_scale }接收窗口有最大接收窗口和當(dāng)前可用接收窗口。
一般來(lái)說(shuō)一次中斷基本都會(huì)將 buffer 中的包都取走。
綠線是最大接收窗口動(dòng)態(tài)調(diào)整的過(guò)程,最開(kāi)始是146010,握手完畢后略微調(diào)整到147210(可利用body增加了12),隨著數(shù)據(jù)的傳輸開(kāi)始跳漲。
上圖是四個(gè)batch insert語(yǔ)句,可以看到綠色接收窗口隨著數(shù)據(jù)的傳輸越來(lái)越大,圖中藍(lán)色豎直部分基本表示SQL上傳,兩個(gè)藍(lán)色豎直條的間隔代表這個(gè)insert在服務(wù)器上真正的執(zhí)行時(shí)間。這圖非常陡峭,表示上傳沒(méi)有任何瓶頸。
設(shè)置 SO_RCVBUF 后通過(guò)wireshark觀察到的接收窗口基本
下圖是設(shè)置了 SO_RCVBUF 為8192的實(shí)際情況:
從最開(kāi)始的14720,執(zhí)行第一個(gè)create table語(yǔ)句后降到14330,到真正執(zhí)行batch insert就降到了8192*1.5. 然后一直保持在這個(gè)值。
If you set a "receive buffer size" on a TCP socket, what does it actually mean?
The naive answer would go something along the lines of: the TCP receive buffer setting indicates the maximum number of bytes a read() syscall could retrieve without blocking.
Note that if the buffer size is set with setsockopt(), the value returned with getsockopt() is always double the size requested to allow for overhead. This is described in man 7 socket.
總結(jié)
- 一般來(lái)說(shuō)絕對(duì)不要在程序中手工設(shè)置SO_SNDBUF和SO_RCVBUF,內(nèi)核自動(dòng)調(diào)整比你做的要好;
- SO_SNDBUF一般會(huì)比發(fā)送滑動(dòng)窗口要大,因?yàn)榘l(fā)送出去并且ack了的才能從SO_SNDBUF中釋放;
- TCP接收窗口跟SO_RCVBUF關(guān)系很復(fù)雜;
- SO_RCVBUF太小并且rtt很大的時(shí)候會(huì)嚴(yán)重影響性能;
- 接收窗口比發(fā)送窗口復(fù)雜多了;
- 發(fā)送窗口/SO_SNDBUF--發(fā)送倉(cāng)庫(kù),帶寬/擁塞窗口--馬路通暢程度,接收窗口/SO_RCVBUF--接收倉(cāng)庫(kù);
- 發(fā)送倉(cāng)庫(kù)、馬路通暢程度、接收倉(cāng)庫(kù)一起決定了傳輸速度--類比一下快遞過(guò)程。
總之記住一句話:不要設(shè)置socket的SO_SNDBUF和SO_RCVBUF。
iPhone 11 Pro、衛(wèi)衣、T恤等你來(lái)抽,馬上來(lái)試試手氣 https://www.aliyun.com/1111/2019/m-lottery?utm_content=g_1000083877
原文鏈接
本文為云棲社區(qū)原創(chuàng)內(nèi)容,未經(jīng)允許不得轉(zhuǎn)載。
總結(jié)
以上是生活随笔為你收集整理的直击案发现场!TCP 10倍延迟的真相是?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 阿里科学家再获世界级荣誉,平头哥首席科学
- 下一篇: 离屏渲染在车载导航中的应用