restTemplate超时时间引发的生产事故
1、现象:
今天下午公司客户群里突然报警,说订单没有推送服务商,经排查发现是rabbitmq堵住了,查询elk和监控没有发现业务异常或超时日志。
通过rabbitmq后面发现一个队列有异常,队列不ack,未消费数量一直增长,怀疑服务假死,只能保留现场,重启服务,下面是服务重启前后队列截图
2、分析
为什么服务重启后队列立马消费一空,证明三方服务商的接口没有问题,经过代码查找发现调用三方用的是restTemplate,核心代码如下:
1 @Service 2 public class OrderStatusChangePushService { 3 private static Logger logger = LoggerFactory.getLogger(OrderStatusChangePushService.class); 4 @Autowired 5 private RestTemplate restTemplate; 6 7 public ResponseEntity<PlatformBaseResponse> notify2Thirdparty(OrderInfoReqs orderInfo, String callbackUrl, AssortmentOpenApiEncryptDto encryptConfig, String operateType) { 8 PlatformBaseRequest request = getRequest(orderInfo, encryptConfig, operateType); 9 HttpHeaders headers = new HttpHeaders(); 10 headers.setContentType(MediaType.APPLICATION_JSON); 11 HttpEntity<PlatformBaseRequest> platformBaseRequestHttpEntity = new HttpEntity<>(request, headers); 12 ResponseEntity<PlatformBaseResponse> exchange = null; 13 exchange = restTemplate.exchange(callbackUrl, HttpMethod.POST, platformBaseRequestHttpEntity, PlatformBaseResponse.class); 14 return exchange; 15 } 16 }
1 @SpringBootApplication 2 public class PlatformOrderConsumerApplication extends SpringBootServletInitializer { 3 @Bean 4 RestTemplate restTemplate() { 5 return new RestTemplate(); 6 } 7 8 public static void main(String[] args) { 9 SpringApplication.run(PlatformOrderConsumerApplication.class, args); 10 } 11 }
发现restTemplate用的是直接new的,未重写连接池也未设置超时时间。看源码得知底层用的jdk的httpurlconnection,若readTimeOut和connectionTimeOut没有设置,那请求是没有超时时间的,导致请求一直hang住。
3、结论
restTemplate没有设置超时时间,导致单挑消息不能ack,hang住了整个队列(因为业务需求,一个队列只能单线程消费)
restTemplate如果没有重写连接池,默认用的SimpleClientHttpRequestFactory,SimpleClientHttpRequestFactory默认的readTimeOut=-1,connectionTimeOut=-1, 导致jdk的HttpUrlConnection是不会超时,进而hang死整个队列。