問題出在哪里:TCP協議有一個TCP_NODELAY 參數會引發延遲。
調用方使用 Apache HTTPClient tcpNoDelay 默認 true ,
被調用方使用HTTP服務,用的JDK自帶的HttpServer 在ServerConfig 中發現 noDelay 默認 false
static { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { ServerConfig.idleInterval = Long.getLong("sun.net.httpserver.idleInterval", 30L) * 1000L; ServerConfig.clockTick = Integer.getInteger("sun.net.httpserver.clockTick", 10000); ServerConfig.maxIdleConnections = Integer.getInteger("sun.net.httpserver.maxIdleConnections", 200); ServerConfig.drainAmount = Long.getLong("sun.net.httpserver.drainAmount", 65536L); ServerConfig.maxReqHeaders = Integer.getInteger("sun.net.httpserver.maxReqHeaders", 200); ServerConfig.maxReqTime = Long.getLong("sun.net.httpserver.maxReqTime", -1L); ServerConfig.maxRspTime = Long.getLong("sun.net.httpserver.maxRspTime", -1L); ServerConfig.timerMillis = Long.getLong("sun.net.httpserver.timerMillis", 1000L); ServerConfig.debug = Boolean.getBoolean("sun.net.httpserver.debug"); ServerConfig.noDelay = Boolean.getBoolean("sun.net.httpserver.nodelay"); return null; } }); }
解決:后端HTTP服務,加上啟動項“Dsun.net.httpserver.nodelay=true”參數,問題解決。
原因:
1.TCP_NODELAY 是什么?
在socket編程中,TCP_NODELAY是用來控制是否開啟Nagle算法的。true 表示關閉 Nagle算法 false則表示打開。
2.Nagle算法是什么?
Nagle算法是一種通過減少發送網絡數據包數量來提高TCP/IP網絡效率的方法。
思路:如果一個請求很小1字節 發送這1字節有效數據的數據包需要40字的包頭(IP頭部20字節,TCP頭部20字節),這種方式利用率很低。
Nagle做法:
如果發送內容大小>= 1MSS立刻發送 ,(MSS 為 TCP 數據包每次能夠傳輸的最大數據分段)
如果之前的包沒有被ACK 立即發送,
如果之前報被ACK 緩存發送部分,
如果收到ACK立刻發送緩存內容。
3.Delayed ACK是什么呢?
思路:TCP 為了保證傳輸的可靠性,規定接收到數據包時候向對方發送一個確認,只發送一個確認代價比較高 跟發1字節的請求一個道理。
TCP Delayed ACK(延遲確認) :將幾個ACK組合在一起的單個響應,或者將ACK與響應數據一起發送給對方,從而減少協議開銷。
具體做法:
當有響應數據發送時,ACK會隨響應數據一起發送給對方。
如果沒有響應數據,ACK將延遲發送,看是否有其他響應數據一起發送, linux上這個延遲是40ms.
如果等待發送ACK期間對方的第二個數據包又到達了,則立即發送ACK,這時如果第三個數據包相繼到達 是否發送ACK 要看前兩條
4.Nagle 和 Delayed ACK一起會發生什么?
A 使用Nagle B 使用Delayed ACK
A 向 B 發送一個包 由於B Delayed ACK 就等着 A再發 這時候 A 也在等着B返回ACK 導致白白等40ms。