上周产品那边反馈说拉取不到2022-01-30的美团第三方账单,于是果断查了下数据库,果然发现数据库中并没有2022-01-30的美团账单记录,分别查了下29号和31号后发现都是有的,就唯独30号的没有。当时我就纳闷了,不就是一个简单的拉取第三方账单的逻辑吗?怎么就会拉取不到呢?为了能够彻底解决我的疑惑,于是我马上去阿里云查了相关拉取日志,不查不知道,一查吓一跳,没想到拉取时还果真出问题了,见下图:
从上图中大家不难发现日志中明显报了java.net.SocketTimeoutException: Read timed out这个错误,那么这个问题是如何产生的呢?我后续又是如何解决的呢?下面听我一一道来。
(1)What Is “Read Timed Out”?
The read() method call in the InputStream blocks until it finishes reading data bytes from the socket、The operation waits until it reads at least one data byte from the socket、However, if the method doesn’t return anything after an unspecified time, it throws an InterrupedIOException with a “Read timed out” error message:java.net.SocketTimeoutException: Read timed out
(2)Why It Occurs?
From the client side, the “read timed out” error happens if the server is taking longer to respond and send information、This could be due to a slow internet connection, or the host could be offline.
From the server side, it happens when the server takes a long time to read data compared to the preset timeout.
将上文翻译过来不难发现,在完成从scoket中读取数据字节之前,调用InputStream块的read方法时是会阻塞的。从客户端的角度来看,如果服务器需要更长的时间来响应和发送信息,Read timed out的报错可能就会发生;从服务端的角度来看,跟预设的超时时间相比,如果服务器需要更长的时间来读取数据的话,那么Read timed out的报错可能也会发生。
搞懂了基本概念之后,紧接着我们需要开始排查这个错误是如何产生的。首先我从在服务端(美团)的角度思考,是不是美团那边在获取30日账单时哪个地方报错导致超时了呢?于是联系了美团的客服沟通发现它那边其实是有返回的,只不过响应时间为7点多秒让我当时感到有点惊讶。再从客户端(我)这边的角度思考,当时我想SocketTimeoutException的产生是不是就跟SocketTimeout的配置值有关呢?为了验证我的想法,于是疯狂找对应代码,居然还真给我找到了,如下图所示:
从上面的配置信息中我们可知道美团提供的sdk中配置的socketTimeOut的值为5000毫秒(5秒)。30日的账单美团那边响应的时间为7点多秒了,都已经超过了5秒了,不报SocketTimeoutException才怪呢?哈哈,美团不是在自己坑自己吗?
既然都已经把这个报错的来龙去脉都摸透了,那么接下来问题就可以迎刃而解了。我的解决方案就是把socketTimeOut这个属性弄成了nacos的配置,并设定默认值为1000ms(10秒)。这样不但解决了当前的问题,而且还可以很好地避免了以后的账单拉取时间超过了10秒后还需要修改代码重新发版的尴尬情形。相关的代码我都贴出来了,第一个代码就是获取socketTimeOut的nacos配置逻辑,第二个代码就是将获取的nacos配置值放到请求RequestConfig.Builder类中传给美团那边 大家可以看下:
private Integer getMTSocketTimeout() { return zzNacosReader.getConfigInteger(ZzNacosConfig.THIRDPARTY_MT_SOCKET_TIMEOUT_CONFIG, 10000); }
private static RequestConfig.Builder handleSocketTimeout(RequestConfig.Builder builder, Integer socketTimeout) { builder.setSocketTimeout(socketTimeout); return builder; }
经过这么一折腾后终于将这个bug修复!希望此篇文章能给你带来启发和帮助!
参考文章:
Connection Timeout vs、Read Timeout for Java Sockets