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死整個隊列。
