前言
今天真是美好的一天呢,股市一如往常地绿,我一如往常地坐在办公桌前敲代码。
上级交给我一个刷新历史数据的需求,一般刷新历史数据可以采用的方式有SQL脚本以及定时任务。
以往大多数时候我都是采用SQL脚本的方式,因为这次涉及接口的调用,于是打算使用后台任务来实现。
本来很日常的一个任务,没想到还会有意外的插曲。
过程
需求
根据新的业务逻辑修复历史数据,就是更新某些表的某些字段。
这里的业务就不详细介绍了。反正与技术无关。
设计和实现
1、将 需要更新的主表的主键 通过脚本刷到临时表,初始化为待处理状态。
(这样做是为了修复历史数据时不影响到其他业务对主表的操作)
2、创建临时定时任务,用代码实现取数的逻辑,其中涉及两次内部接口的调用。
3、批量处理完数据后,更新主表数据以及临时表的状态。
4、将接口的调用记录到临时日志表,主要字段有主表主键、接口标志、发起请求时间、返回结果时间、返回数据
测试结果
上面的步骤一气呵成,自以为胸有成竹。当然开发机也有自测过了,流程是没问题的。
于是提交给测试部进行测试,此时已经接近下班时间了。
结果测试告知数据跑着跑着就卡住了,待处理的数据量没有继续减少了。
想想心都凉了,这是要加班的节奏呀,于是连忙开始分析。
查看了临时表,待处理的数据量确实没有继续减少了;
再查看临时日志表,按时间降序后,发现大多数接口的调用间隔时间都是毫秒级别的,但是极少数调用的间隔时间很长,大到几分钟。现在就是卡死不动了,也没新的日志产生。
那时候怀疑是逻辑问题,于是检查了一遍,确实找不出问题,因为本身这个任务的处理逻辑也不复杂。
于是我重启了测试机的定时任务,发现又好使了。。。
分析
从日志看确实没打印有效的信息,这可能是我日志埋点埋得不好。我的锅。
根据这现象,在网上查找了一些资料以及和同事讨论后,锁定了一个方向,那就是跟请求接口有关的RestTemplate
。
在网络上其中有一篇文章《记录RestTemplate一个自己挖的坑》如下分析:
另外我单独测了任务中所调用的接口,响应时间平均下来也是毫秒级别。
所以越来越确定是由于Http请求造成这次任务的异常。
查看了这次任务中使用的请求工具,也就是RestTemplate
。
以前倒没怎么注意这个小家伙,这次找了一些关于它的资料。
解决
解决方法就是为RestTemplate
设置超时时间,避免过长的无响应连接所带来的耗时影响。
连接失败则断开,再进行重连即可。
原先使用的默认RestTemplate
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate" />
设置了超时的RestTemplate
<!-- 同步restful,3s超时 -->
<bean id="restTemplate3sTimeout" class="org.springframework.web.client.RestTemplate">
<constructor-arg>
<bean class="org.springframework.http.client.HttpComponentsClientHttpRequestFactory">
<property name="readTimeout" value="3000"/>
<property name="connectTimeout" value="3000"/>
</bean>
</constructor-arg>
</bean>