【原创】经验分享:一个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}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章