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