Broken Pipe
Broken Pipe發(fā)生的原因
當(dāng)某個(gè)進(jìn)程試圖往一個(gè)已收到RST的SOCKET連接寫數(shù)據(jù),就會(huì)出現(xiàn)Broken Pipe。
(由于TCP協(xié)議層已經(jīng)處于RST狀態(tài)了,因此不會(huì)將數(shù)據(jù)發(fā)出,而是發(fā)一個(gè)SIGPIPE信號給應(yīng)用層,SIGPIPE信號的缺省處理動(dòng)作是終止程序。)
那么確定什么時(shí)候TCP會(huì)發(fā)送RST報(bào)文段,就可以確定Broken Pipe發(fā)生的具體原因。
之前已經(jīng)分析了TCP RST報(bào)文產(chǎn)生的幾種情景了。
原因分析
broken pipe出現(xiàn)的前提條件是進(jìn)程試圖往一個(gè)已經(jīng)在RST狀態(tài)的TCP連接寫入數(shù)據(jù)。
那么這個(gè)寫入數(shù)據(jù),到底應(yīng)該怎么理解呢?到底是進(jìn)程試圖往本地SendQ發(fā)送緩存區(qū)寫入數(shù)據(jù)還是TCP協(xié)議試圖將SendQ的數(shù)據(jù)發(fā)送到對端的RecvQ呢?按照字面意思應(yīng)該是前者。
之前我們已經(jīng)分析了幾種會(huì)出現(xiàn)RST報(bào)文的情況。
結(jié)合我們出現(xiàn)該異常的接口分析。我們發(fā)現(xiàn)我們出錯(cuò)的接口,返回的數(shù)據(jù),最小的8K多,最大的超過128K。查閱了幾天的異常日志,都沒有發(fā)現(xiàn)一個(gè)報(bào)出broken pipe異常錯(cuò)誤的接口的返回?cái)?shù)據(jù)小于8K。
根據(jù)RST報(bào)文產(chǎn)生的情況,我們可以做出如下推斷,當(dāng)Client端與我們的服務(wù)器建立了TCP鏈接之后。當(dāng)TCP協(xié)議將服務(wù)端SendQ隊(duì)列里的內(nèi)容發(fā)送到對端(Client)的ReceQ隊(duì)列中后,Client關(guān)閉了進(jìn)程,此時(shí)ReceQ 讀取緩存區(qū)還有數(shù)據(jù)未被讀取(不管ReceQ的數(shù)據(jù)Client端有沒有讀取過,也不管TCP將多少服務(wù)端的數(shù)據(jù)發(fā)到了ReceQ,總之,就是在關(guān)閉的時(shí)候,還有數(shù)據(jù)存在于讀取緩存區(qū)中),這時(shí)候關(guān)閉socket,會(huì)導(dǎo)致端Client端產(chǎn)生一個(gè)RST重置報(bào)文。
這時(shí)候服務(wù)端的數(shù)據(jù)還沒有寫完,會(huì)繼續(xù)寫入,當(dāng)再次寫入的時(shí)候,TCP協(xié)議已經(jīng)是RST狀態(tài)的了,這個(gè)時(shí)候,就會(huì)發(fā)生broken pipe。
上面的推論能夠解釋broken pipe發(fā)生的一整個(gè)流程。
那我們來查看下linux服務(wù)器的默認(rèn)SendQ大小與我們接口返回的數(shù)據(jù)大小的對比,就能夠是否確實(shí)是這些接口的數(shù)據(jù)寫入都需要多次寫入緩存區(qū)。
那么如何查看linux默認(rèn)的SendQ緩沖區(qū)大小呢。linux給我們提供了相應(yīng)的命令
ubuntu@VM-104-50-ubuntu:/data/iyourcar$ cat /proc/sys/net/ipv4/tcp_wmem 4096 16384 4194304最小 默認(rèn) 最大我們發(fā)現(xiàn),默認(rèn)寫緩存區(qū)大小是16k,可是我們的接口返回的數(shù)據(jù)最小的是8k多呀,這個(gè)接口返回的數(shù)據(jù)是可以一次寫入緩存區(qū)的呀。咋回事呀,怎么這樣也會(huì)broken呢。如果服務(wù)端一次性寫入16k數(shù)據(jù)到寫緩存區(qū),那么是不可能出現(xiàn)broken pipe的呀。那只能證明我們的程序并不是一次性寫入16k的數(shù)據(jù)給緩存區(qū),這個(gè)大小肯定是要比8k多要小的。那我們就來求證一下,用一個(gè)會(huì)報(bào)出異常的接口在出現(xiàn)異常的地方進(jìn)行debug,主要debug寫入數(shù)據(jù)的流程。
使用的內(nèi)置容器是Tomcat
OutputBuffer#appendByteArray
public static final int DEFAULT_BUFFER_SIZE = 8 * 1024;public OutputBuffer() {this(DEFAULT_BUFFER_SIZE);}private void appendByteArray(byte src[], int off, int len) throws IOException {if (len == 0) {return;}int limit = bb.capacity();//我們發(fā)現(xiàn),每一次寫入的字節(jié)數(shù)bb.capacity()大小,而默認(rèn)的capacity大小就是8*1024,也就是8kwhile (len >= limit) {realWriteBytes(ByteBuffer.wrap(src, off, limit));len = len - limit;off = off + limit;}if (len > 0) {transfer(src, off, len, bb);}}我們發(fā)現(xiàn),實(shí)際上,tomcat幫我們向socket寫入數(shù)據(jù)的時(shí)候,是每8k寫入一次SendQ(但是真正TCP發(fā)送數(shù)據(jù),可能是分很多塊去發(fā)送到對端的ReceQ的)
后來我們將內(nèi)置web容器換成了undertow,發(fā)現(xiàn)依舊會(huì)發(fā)生這種情況,應(yīng)該默認(rèn)也是一次寫入8k吧,具體還沒有debug。
所以當(dāng)使用的容器是tomcat的時(shí)候,只要接口返回?cái)?shù)據(jù)的大小大于8k,就可能會(huì)出現(xiàn)broken pipe。
總結(jié)
以上是生活随笔為你收集整理的Broken Pipe的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 扫描二维码进小程序指定页面
- 下一篇: 苹果六电池_苹果新产品发布,这次加量不加