【原創】經驗分享:一個Content-Length引發的血案(almost....)

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"前言"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上週在工作中遇到一個問題,挺有意思,這裏記錄一下。上週在工作中遇到一個問題,挺有意思,這裏記錄一下。標題起的很唬人,這個問題差點引發血案,"},{"type":"text","marks":[{"type":"strong"}],"text":"花哥"},{"type":"text","text":"還是很嚴謹的一個人,後面備註了"},{"type":"codeinline","content":[{"type":"text","text":"almost...."}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在測試環境中,前端調用我們服務一個接口時發現巨慢無比,響應時間超過了"},{"type":"text","marks":[{"type":"strong"}],"text":"30s"},{"type":"text","text":",簡直無法忍受!!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"查看日誌顯示是我們服務在通過"},{"type":"codeinline","content":[{"type":"text","text":"Feign"}]},{"type":"text","text":"請求調用另一個服務的"},{"type":"codeinline","content":[{"type":"text","text":"GET"}]},{"type":"text","text":"接口時一直超時,然後重試了一直直到失敗。 但是奇怪的是手動通過"},{"type":"codeinline","content":[{"type":"text","text":"ip+端口"}]},{"type":"text","text":"請求這個超時的"},{"type":"codeinline","content":[{"type":"text","text":"GET"}]},{"type":"text","text":"接口時卻響應速度很快。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這就很奇怪了,之前一直調用好好的接口,怎麼現在就一直超時呢?"},{"type":"text","marks":[{"type":"strong"}],"text":"此時的我是滿腦子問號。。。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"現象"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"前端調用我們服務(這裏叫做"},{"type":"codeinline","content":[{"type":"text","text":"服務A"}]},{"type":"text","text":")的一個查詢接口,這裏前端用的是"},{"type":"codeinline","content":[{"type":"text","text":"POST"}]},{"type":"text","text":"請求,我們服務又會通過"},{"type":"codeinline","content":[{"type":"text","text":"Feign"}]},{"type":"text","text":"調用到另一個服務(這裏叫做"},{"type":"codeinline","content":[{"type":"text","text":"服務B"}]},{"type":"text","text":")的一個接口,這個接口對外提供"},{"type":"codeinline","content":[{"type":"text","text":"GET"}]},{"type":"text","text":"形式的調用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從現象上來看就是調用我們服務特別慢,一個請求響應幾十秒,具體流程如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d1/d1847ab03e43155d011d3c01f236d8dd.png","alt":"請求流程.png","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"問題排查"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當時腦子中出現的疑惑就是太奇怪了,之前一隻調用的接口不應該會出現這種情況,而且手動通過"},{"type":"codeinline","content":[{"type":"text","text":"ip+端口"}]},{"type":"text","text":"去調用的話響應速度很快的,於是找了"},{"type":"codeinline","content":[{"type":"text","text":"服務B"}]},{"type":"text","text":"對外開發的同學一起看,因爲自己忽略了一些重要的日誌信息,所以這裏走了不少彎路,在同事的幫助下自己也將這個問題梳理清楚了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"問題的根本原因是我們在"},{"type":"codeinline","content":[{"type":"text","text":"GET"}]},{"type":"text","text":"請求的"},{"type":"codeinline","content":[{"type":"text","text":"Header"}]},{"type":"text","text":"中傳遞了"},{"type":"codeinline","content":[{"type":"text","text":"Content-Length"}]},{"type":"text","text":"參數,而且服務B近期添加了一個"},{"type":"codeinline","content":[{"type":"text","text":"jar"}]},{"type":"text","text":"包,"},{"type":"codeinline","content":[{"type":"text","text":"jar"}]},{"type":"text","text":"中有一個攔截器做了一些事情導致了這個問題。我這裏從"},{"type":"text","marks":[{"type":"strong"}],"text":"源碼層面"},{"type":"text","text":"上梳理下整個問題的根本原因,以及以後如何避免此類問題!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於這個問題,自己本地分別啓動"},{"type":"codeinline","content":[{"type":"text","text":"服務A"}]},{"type":"text","text":"和"},{"type":"codeinline","content":[{"type":"text","text":"服務B"}]},{"type":"text","text":",以"},{"type":"codeinline","content":[{"type":"text","text":"DEBUG"}]},{"type":"text","text":"模式啓動,發現可以穩定重現,而且可以看到在調用"},{"type":"codeinline","content":[{"type":"text","text":"服務B"}]},{"type":"text","text":"卡住時候的"},{"type":"text","marks":[{"type":"strong"}],"text":"堆棧信息"},{"type":"text","text":":"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/05/056d8a2e523ce742fc50fea7b7ed2d4f.png","alt":"線程堆棧信息.png","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"服務A"}]},{"type":"text","text":"發起的請求卡住的原因是在"},{"type":"codeinline","content":[{"type":"text","text":"awaitLatch()"}]},{"type":"text","text":"被掛起了,到了這裏纔算是找到了問題原因的突破口,下面繼續往上一步步跟蹤就可以找到問題的所在了,下面會一步步認真分析。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"問題原因"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏問題的原因其實是通過上面問題排查反推出來的:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"前端調用服務端接口時,因爲是"},{"type":"codeinline","content":[{"type":"text","text":"post"}]},{"type":"text","text":"請求,所以"},{"type":"codeinline","content":[{"type":"text","text":"header"}]},{"type":"text","text":"中傳遞的有"},{"type":"codeinline","content":[{"type":"text","text":"Content-Length"}]},{"type":"text","text":"屬性,調用"},{"type":"codeinline","content":[{"type":"text","text":"feign"}]},{"type":"text","text":"請求時,不論"},{"type":"codeinline","content":[{"type":"text","text":"get"}]},{"type":"text","text":"還是"},{"type":"codeinline","content":[{"type":"text","text":"post"}]},{"type":"text","text":"請求,公司底層包中有個"},{"type":"codeinline","content":[{"type":"text","text":"Feign"}]},{"type":"text","text":"攔截器會將前端請求"},{"type":"codeinline","content":[{"type":"text","text":"Header"}]},{"type":"text","text":"屬性賦值給"},{"type":"codeinline","content":[{"type":"text","text":"feign"}]},{"type":"text","text":"請求中的"},{"type":"codeinline","content":[{"type":"text","text":"Header"}]},{"type":"text","text":",導致我們發送的"},{"type":"codeinline","content":[{"type":"text","text":"GET"}]},{"type":"text","text":"請求"},{"type":"codeinline","content":[{"type":"text","text":"Header"}]},{"type":"text","text":"中也含有"},{"type":"codeinline","content":[{"type":"text","text":"Content-Length"}]},{"type":"text","text":"屬性。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ps: 這一點很坑,依賴的底層包加了一個Feign攔截器,我們是通過打印feign請求日誌在控制檯纔看到Content-Length屬性的,最後跟蹤到這個FeignInterceptor中的"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"2","normalizeStart":"2"},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"服務B剛好依賴了另一個"},{"type":"codeinline","content":[{"type":"text","text":"jar"}]},{"type":"text","text":"包,該包中包含一個"},{"type":"codeinline","content":[{"type":"text","text":"Filter"}]},{"type":"text","text":"攔截器,它會讀取發送的請求"},{"type":"codeinline","content":[{"type":"text","text":"body"}]},{"type":"text","text":"數據,然後做一些日誌打印。而且這個"},{"type":"codeinline","content":[{"type":"text","text":"jar"}]},{"type":"text","text":"包依賴也是他們剛加的,他們使用該包中的其他一些工具類"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class ChannelFilter implements Filter {\n public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {\n if (servletRequest instanceof HttpServletRequest) {\n requestWrapper = new RequestWrapper((HttpServletRequest)servletRequest);\n log.info(\"Http RequestURL : {}, Method : {}, RequestParam : {}, RequestBody : {}\", new Object[]{((HttpServletRequest)servletRequest).getRequestURL(), ((HttpServletRequest)servletRequest).getMethod(), JSON.toJSON(servletRequest.getParameterMap()), ((RequestWrapper)requestWrapper).getBody()});\n }\n\n\n filterChain.doFilter((ServletRequest)requestWrapper, servletResponse);\n }\n\n\n public void destroy() {\n }\n}\n\npublic class RequestWrapper extends HttpServletRequestWrapper {\n private static final Logger log = LoggerFactory.getLogger(RequestWrapper.class);\n private final String body;\n\n public RequestWrapper(HttpServletRequest request) {\n super(request);\n StringBuilder stringBuilder = new StringBuilder();\n BufferedReader bufferedReader = null;\n ServletInputStream inputStream = null;\n\n try {\n inputStream = request.getInputStream();\n if (inputStream != null) {\n bufferedReader = new BufferedReader(new InputStreamReader(inputStream));\n char[] charBuffer = new char[4096];\n boolean var6 = true;\n\n int bytesRead;\n while((bytesRead = bufferedReader.read(charBuffer)) != -1) {\n stringBuilder.append(charBuffer, 0, bytesRead);\n }\n }\n } catch (IOException var19) {\n log.error(var19.getMessage(), var19);\n }\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在執行"},{"type":"codeinline","content":[{"type":"text","text":"request body"}]},{"type":"text","text":"讀取的代碼時使用到:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"while((bytesRead = bufferedReader.read(charBuffer)) != -1) {\n stringBuilder.append(charBuffer, 0, bytesRead);\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"bufferedReader.read()"}]},{"type":"text","text":"最終會調用到"},{"type":"codeinline","content":[{"type":"text","text":"Tomcat"}]},{"type":"text","text":" 中"},{"type":"codeinline","content":[{"type":"text","text":"org.apache.tomcat.util.net.NioBlockingSelector.read()"}]},{"type":"text","text":"的方法讀取"},{"type":"codeinline","content":[{"type":"text","text":"request"}]},{"type":"text","text":"中的"},{"type":"codeinline","content":[{"type":"text","text":"body"}]},{"type":"text","text":"屬性:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"int keycount = 1; \nwhile(!timedout) {\n if (keycount > 0) { //only read if we were registered for a read\n read = socket.read(buf);\n if (read != 0) {\n break;\n }\n }\n try {\n if ( att.getReadLatch()==null || att.getReadLatch().getCount()==0) att.startReadLatch(1);\n poller.add(att,SelectionKey.OP_READ, reference);\n if (readTimeout < 0) {\n att.awaitReadLatch(Long.MAX_VALUE, TimeUnit.MILLISECONDS);\n } else {\n att.awaitReadLatch(readTimeout, TimeUnit.MILLISECONDS);\n }\n } catch (InterruptedException ignore) {\n // Ignore\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏因爲"},{"type":"codeinline","content":[{"type":"text","text":"GET"}]},{"type":"text","text":"請求的"},{"type":"codeinline","content":[{"type":"text","text":"body"}]},{"type":"text","text":"爲空,所以"},{"type":"codeinline","content":[{"type":"text","text":"socket.read()"}]},{"type":"text","text":" 返回爲0,進而走到"},{"type":"codeinline","content":[{"type":"text","text":"att.awaitReadLatch(readTimeout, TimeUnit.MILLISECONDS)"}]},{"type":"text","text":";"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"protected void awaitLatch(CountDownLatch latch, long timeout, TimeUnit unit) throws InterruptedException {\n if ( latch == null ) throw new IllegalStateException(\"Latch cannot be null\");\n latch.await(timeout,unit);\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏就會調用到"},{"type":"codeinline","content":[{"type":"text","text":"LockSuport.parkNanos(time)"}]},{"type":"text","text":" 接口 直到超時,此時的你們會不會仍然有疑惑,爲什麼"},{"type":"codeinline","content":[{"type":"text","text":"Header"}]},{"type":"text","text":"中傳遞了"},{"type":"codeinline","content":[{"type":"text","text":"Content-Length"}]},{"type":"text","text":"就會走這個邏輯鏈路呢?別急,繼續往下看,後面還有更精彩的分析......"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"解決方案"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"服務B"}]},{"type":"text","text":"取消有問題"},{"type":"codeinline","content":[{"type":"text","text":"jar"}]},{"type":"text","text":"包的依賴"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"修改問題"},{"type":"codeinline","content":[{"type":"text","text":"jar"}]},{"type":"text","text":"包中"},{"type":"codeinline","content":[{"type":"text","text":"Filter"}]},{"type":"text","text":"的配置,判斷只有"},{"type":"codeinline","content":[{"type":"text","text":"Post"}]},{"type":"text","text":"請求才去讀取"},{"type":"codeinline","content":[{"type":"text","text":"body"}]},{"type":"text","text":"屬性"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"接口調用方添加配置如果是"},{"type":"codeinline","content":[{"type":"text","text":"GET"}]},{"type":"text","text":"請求時過濾掉"},{"type":"codeinline","content":[{"type":"text","text":"Content-Length"}]},{"type":"text","text":"屬性(主要原因)"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"修改底層依賴包"},{"type":"codeinline","content":[{"type":"text","text":"FeignInterceptor"}]},{"type":"text","text":",判斷請求的方式然後再針對"},{"type":"codeinline","content":[{"type":"text","text":"Header"}]},{"type":"text","text":"賦值(公司底層依賴的包我們不太好修改)"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其實最應該修改的是方案4,只是這個是全公司都會依賴的一個底層包,如果改動起來需要通知架構組等等,而且影響面會比較大。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最終我們先採用方案3,在我們請求鏈路中去做一些判斷,去除"},{"type":"codeinline","content":[{"type":"text","text":"GET"}]},{"type":"text","text":"請求中"},{"type":"codeinline","content":[{"type":"text","text":"Content-Length"}]},{"type":"text","text":"的傳遞。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"解決原理"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下來就是真正原理的地方了,當服務端發出"},{"type":"codeinline","content":[{"type":"text","text":"feign"}]},{"type":"text","text":"請求後,一定會走"},{"type":"codeinline","content":[{"type":"text","text":"Tomcat"}]},{"type":"text","text":"中的"},{"type":"codeinline","content":[{"type":"text","text":"org.apache.coyote.http11.Http11Processor.prepareRequest()"}]},{"type":"text","text":"方法,代碼如圖:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a7/a7b1e4be0c2b98032d226a464d5a3dbb.png","alt":"Http11Processor.prepareRequest().png","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果"},{"type":"codeinline","content":[{"type":"text","text":"contentLength >= 0"}]},{"type":"text","text":",那麼會添加一個"},{"type":"codeinline","content":[{"type":"text","text":"org.apache.coyote.http11.filters.IdentityInputFilter"}]},{"type":"text","text":"類,在"},{"type":"codeinline","content":[{"type":"text","text":"服務B"}]},{"type":"text","text":"添加的"},{"type":"codeinline","content":[{"type":"text","text":"jar"}]},{"type":"text","text":"包中的"},{"type":"codeinline","content":[{"type":"text","text":"RequestWrapper"}]},{"type":"text","text":"中的"},{"type":"codeinline","content":[{"type":"text","text":"bufferedReader.read()"}]},{"type":"text","text":"會調用到 "},{"type":"codeinline","content":[{"type":"text","text":"org.apache.coyote.http11.filters.IdentityInputFilter.doRead()"}]},{"type":"text","text":" 方法:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/40/407020180fcee6380f43fd39a657db29.png","alt":"wE7F6s.png","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個方法又會直接調用到 "},{"type":"codeinline","content":[{"type":"text","text":"org.apache.tomcat.util.net.NioBlockingSelector.read()"}]},{"type":"text","text":"中:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/e7/e7ebf0b131e261dd4cc101a43887ee26.png","alt":"NioBlockingSelector.read().png","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因爲"},{"type":"codeinline","content":[{"type":"text","text":"GET"}]},{"type":"text","text":"請求的"},{"type":"codeinline","content":[{"type":"text","text":"request body"}]},{"type":"text","text":"爲空,所以這裏通過"},{"type":"codeinline","content":[{"type":"text","text":"socket"}]},{"type":"text","text":"去讀取時返回爲0,直接運行下面的"},{"type":"codeinline","content":[{"type":"text","text":"awaitReadLatch()"}]},{"type":"text","text":" 方法,這裏會調用"},{"type":"codeinline","content":[{"type":"text","text":"LockSuport.parkNanos(time)"}]},{"type":"text","text":" 接口 直到超時,這也是爲什麼我們每次"},{"type":"codeinline","content":[{"type":"text","text":"feign"}]},{"type":"text","text":"請求都會超時的原因。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但是如果服務請求方配置了傳遞的"},{"type":"codeinline","content":[{"type":"text","text":"Content-Length"}]},{"type":"text","text":"爲空呢?這裏會構造一個"},{"type":"codeinline","content":[{"type":"text","text":"org.apache.coyote.http11.filters.VoidInputFilter"}]},{"type":"text","text":",這個攔截器的構造在上面"},{"type":"codeinline","content":[{"type":"text","text":"Http11Processor.prepareRequest()"}]},{"type":"text","text":"圖示中已經標明:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/e7/e7ebf0b131e261dd4cc101a43887ee26.png","alt":"NioBlockingSelector.read().png","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"顯而易見,這裏直接返回-1,不會再去調用"},{"type":"codeinline","content":[{"type":"text","text":"NioBlockingSelector.read()"}]},{"type":"text","text":" 方法了,所以成功解決此問題,這也是問題的關鍵所在。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"總結"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏沒有過多的去介紹"},{"type":"codeinline","content":[{"type":"text","text":"Content-Length"}]},{"type":"text","text":"的概念,默許大家都知道這個,如果不太清楚的還可以參考:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"https://blog.piaoruiqing.com/2019/09/08/do-you-know-content-length/"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一個簡單的"},{"type":"codeinline","content":[{"type":"text","text":"Content-Length"}]},{"type":"text","text":"確實難住了我,請求的不規範纔是這次問題的真正原因。而排查出來這個問題也花費了很多時間,不過這些都是挺值得的,一個人的成長離不開各種問題的洗禮,希望大家閱讀完也會有所收穫。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"歡迎關注:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/11/1115cca6fcac752923b30f3805040567.png","alt":"原創乾貨分享.png","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章