【精选】排查解决RestTemplate发起的POST请求出现卡死的现象

您所在的位置:网站首页 密蜂喂什么糖 【精选】排查解决RestTemplate发起的POST请求出现卡死的现象

【精选】排查解决RestTemplate发起的POST请求出现卡死的现象

2023-11-12 12:23| 来源: 网络整理| 查看: 265

结论陈述

多人开发,创建了多个定时任务用来推送二三十种业务数据,同时也为部分业务数据做了及时推送的机制,采用的restemplate工具类,在一段时间之内 ,系统运行得很正常,突然有一天,测试发现断网之后,很多及时推送的数据就不没有出现在接收方的系统里面,就像连锁反应一样,经排查,原因如下

restemplate调用接口前,线程能打印出日志,开始调用接口后,就没有输出日志,也就是没有响应消息,甚至异常也捕获不到,此现象断定restemplate调用接口出现了阻塞配置了restemplate的超时时间 ,竟然没有生效,找出失效的原因后,成功捕获到超时异常 在这里插入图片描述 问题现象

观察某一次RestTemplate 发起的POST请求卡死

原因分析

一开始怀疑是线程池的可用线程数量不够,加大核心线程池数量、最大线程数量和线程队列数量后,发现阻塞问题依旧。

在查询资料后,发现restemplate是单线程,可能出现阻塞,反思一下,如果配置了超时机制且生效了,那么在第一个接口调用请求之后,就应该正常触发第二个接口调用才对,实际上我们第一个请求都没有闭环,原理上说不通。

当然restemplate的单线程设计也曾质疑过【 Spring 异步HTTP AsyncRestTemplate介绍_zzhongcy的博客-CSDN博客_resttemplate异步还是同步的】,也尝试使用 AsyncRestTemplate改造 RestTemplate,发现 AsyncRestTemplate已经过时了,就不再继续了。

RestTemplate的异步兄弟AsyncRestTemplate。

在 Spring 3 时代,为了能更优雅地实现HTTP调用,引入了 RestTemplate,其中提供了多种便捷访问远程Http服务的方法,能够大大提高客户端的编写效率。

在 Spring 4 时代,为了能实现异步地HTTP调用,引入了AsyncRestTemplate,使得编写异步代码和同步代码一样简单。

AsyncRestTemplate 是 Spring中提供异步的客户端HTTP访问的核心类。与RestTemplate类相似,它提供了一些类似的方法,只不过返回类型不是具体的结果,而是ListenableFuture包装类。

在定位到是restemplate阻塞问题后,开始走查代码,将封装了restemplate的工具类,拿来做单元测试,复现了阻塞问题,这样的做法还是非常有必要,因为很多时候代码不是自己写的,别人留下坑,都复现不出来的。

本人曾一度怀疑是restemplate版本不对导致的restemplate本身的超时机制不生效,然则本人使用与项目中同版本的restemplate进行测试后,发现超过机制是正常的,虽然浪费了时间,好在也排查除了一种可能性。

**千算万算没想到啊,在代码中发现竟然以前有人在配置restemplate支持重定向功能时,把已经配置过的超时赶时间,重新覆盖为空了,直接导致超时机制失效,**而

restTemplate用的是直接new的,未重写连接池也未设置超时时间。看源码得知底层用的jdk的httpurlconnection,若readTimeOut和connectionTimeOut没有设置,那请求是没有超时时间的,导致请求一直hang住。

restTemplate超时时间引发的生产事故 - 编程猎人 (programminghunter.com)

问题代码

采用debug走查代码时,在方法【supportRedirectUrl】中创建的HttpComponentsClientHttpRequestFactory,直接将方法【httpComponentsClientHttpRequestFactory】中配置过的超时时间覆盖掉了

@Bean public RestTemplate restTemplate(HttpComponentsClientHttpRequestFactory httpFactory) { RestTemplate restTemplate = new RestTemplate(httpFactory); restTemplate.setErrorHandler(new ResponseErrorHandler() { @Override public boolean hasError(ClientHttpResponse clientHttpResponse) { return false; } @Override public void handleError(ClientHttpResponse clientHttpResponse) { } }); List mediaTypes = new ArrayList(); mediaTypes.add(MediaType.TEXT_PLAIN); mediaTypes.add(MediaType.TEXT_HTML); MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); converter.setSupportedMediaTypes(mediaTypes); restTemplate.getMessageConverters().add(converter); //添加重定向支持 this.supportRedirectUrl(httpsFactory, restTemplate); return restTemplate; } /** * 添加重定向支持 * * @param restTemplate */ private void supportRedirectUrl(RestTemplate restTemplate) { TrustStrategy acceptingTrustStrategy = (x509Certificates, authType) -> true; SSLContext sslContext = null; try { sslContext = SSLContexts .custom() .loadTrustMaterial(null, acceptingTrustStrategy) .build(); } catch ( NoSuchAlgorithmException | KeyManagementException | KeyStoreException e ) { e.printStackTrace(); } SSLConnectionSocketFactory connectionSocketFactory = new SSLConnectionSocketFactory( sslContext, new NoopHostnameVerifier() ); HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(); HttpClient httpClient = HttpClientBuilder .create() .setRedirectStrategy(new LaxRedirectStrategy()) .setSSLSocketFactory(connectionSocketFactory) .build(); factory.setHttpClient(httpClient); restTemplate.setRequestFactory(factory); } @Bean(name = "httpFactory") public HttpComponentsClientHttpRequestFactory httpComponentsClientHttpRequestFactory() throws Exception { RequestConfig defaultRequestConfig = RequestConfig .custom() .setConnectTimeout(ConnectTimeout) .setSocketTimeout(socketTimeout) .setConnectionRequestTimeout(ConnectTimeout) .build(); CloseableHttpClient closeableHttpClient = HttpClients .custom() .setDefaultRequestConfig(defaultRequestConfig) .build(); HttpComponentsClientHttpRequestFactory httpFactory = new HttpComponentsClientHttpRequestFactory( closeableHttpClient ); httpFactory.setReadTimeout(readTimeout); httpFactory.setConnectTimeout(ConnectTimeout); httpFactory.setConnectionRequestTimeout(connectionRequestTimeout); httpFactory.setBufferRequestBody(false); return httpFactory; } 总结一句

项目过程中,往往都是多人协作,意料之外的问题往往不是开发语言的问题,反而人为制造问题更为隐匿,遇到这种不可控的情况 ,平时开发工作过程中,做好code review就显得尤其至关重要,做系统,也好比万丈高楼平地起,在团队成员能力不成熟时,做好核心代码管控+代码评审缺一不可,每个人的一砖一瓦都应当对个人负责,对项目负责,一时的无所谓,欠的“债”,逃不掉,总要还。

业务背景

项目中要求向A系统推送存在多种类型数据,代码中以创建多个定时任务来且异步的方式去推送成千上万的数据。

为了保障数据推送,采用方式有

选择及时性要求很高的部分数据采用及时和定时推送推送服务启动时,推送一次的全部数据 代码实现 底层采用restemplate调用A系统的接口application.properties配置线程池初始化参数 参考资料 Springboot RestTemplate设置超时时间_wtopps的博客-CSDN博客_resttemplate 超时时间设置WebClient 非阻塞客户端 RestTemplate 阻塞式客户端 - 维平 - 博客园 (cnblogs.com) Spring WebClient,异步POST请求代码段_动森万岁的博客-CSDN博客_spring webclient 异步如何使用Arthas定位线上 Dubbo 线程池满异常_程序猿DD_的博客-CSDN博客


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3