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

结论陈述

多人开发,创建了多个定时任务用来推送二三十种业务数据,同时也为部分业务数据做了及时推送的机制,采用的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<MediaType> 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配置线程池初始化参数

参考资料

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: stm32f030f4是意法半导体(STMicroelectronics)推出的一款32位单片机,具有串口和flash读写功能。如果在同时使用串口与flash读写时出现卡死现象,可能是以下几个原因导致的: 1. 中断优先级设置不正确:stm32f030f4有多个外设,包括串口和flash,在同时使用时,需要设置正确的中断优先级,以确保高优先级的中断可以正常执行。如果中断优先级设置不正确,可能会导致卡死现象。 2. 资源冲突:串口和flash都使用了DMA(直接内存访问)来提高数据传输效率,如果DMA通道或缓冲区被同时使用,可能会发生资源冲突,导致卡死。在使用串口和flash时,需要确保DMA资源分配的正确性。 3. 程序逻辑错误:在程序的设计与编写过程中,可能存在逻辑错误,例如死循环、死锁等,导致程序无法正常执行。这种情况下,需要对程序进行仔细的调试和排查,找出错误的原因并进行修正。 针对以上问题,可以尝试以下解决方法: 1. 检查中断优先级设置:修改中断优先级设置,确保高优先级的中断可以正常执行。 2. 确保资源分配的正确性:如果串口和flash同时使用了DMA,需要仔细检查DMA通道和缓冲区的分配是否正确。 3. 调试和排查程序错误:通过添加调试信息、查看日志等方法,找出程序中的逻辑错误,并进行相应的修正。 若以上方法均无法解决卡死现象,建议参考stm32f030f4的相关技术文档,查找更为详细的解决方案,或者联系意法半导体的技术支持团队,寻求进一步的帮助。 ### 回答2: 当STM32F030F4的串口(UART)和Flash(闪存)同时被使用时出现卡死现象,可能是由以下几个原因引起的: 1. 资源冲突:由于串口和Flash使用的IO引脚可能会发生冲突,导致读写操作无法正常进行。此时需要检查串口和Flash的引脚配置,确保它们没有冲突。 2. 中断优先级配置不当:当串口和Flash同时使用时,中断可能会相互干扰,导致卡死现象。在使用中断时,需要正确配置中断优先级,确保串口和Flash的中断能够按照正确的优先级触发和执行。 3. 数据处理不及时:当串口和Flash同时工作时,串口接收到的数据可能积压,而Flash读写操作需要一定的时间。如果处理串口接收数据的任务耗时较长,可能导致读写操作无法及时进行,最终导致卡死现象。此时需要优化数据的处理流程,确保能够及时处理串口接收到的数据。 4. 程序逻辑错误:代码中可能存在逻辑错误或错误的操作顺序,导致串口和Flash同时使用时出现卡死现象。此时需要仔细检查代码,确保程序逻辑正确,并按照正确的顺序使用串口和Flash。 针对以上问题,可以按照以下步骤进行排查解决: 1. 检查串口和Flash的引脚配置,确保它们没有冲突。 2. 配置中断优先级,确保串口和Flash的中断按照正确的优先级触发和执行。 3. 优化数据处理流程,确保能够及时处理串口接收到的数据。 4. 仔细检查代码,确保程序逻辑正确,并按照正确的顺序使用串口和Flash。 通过以上步骤进行排查解决,应该能够解决串口和Flash同时使用时出现卡死现象的问题。 ### 回答3: 首先,stm32f030f4是意法半导体(STMicroelectronics)生产的一款32位ARM Cortex-M0微控制器,具有串口功能和Flash存储器。当串口与Flash读写同时使用时出现卡死现象可能是由于以下几个原因: 1. 资源冲突:串口和Flash模块可能使用了相同的硬件资源,例如DMA通道或中断向量表。如果两个模块同时使用相同的资源,可能会导致资源冲突,从而导致卡死现象。可以通过重新分配资源或调整中断优先级的方式解决这个问题。 2. 中断处理:如果串口和Flash的中断服务程序没有正确编写或配置,可能会导致系统无法响应其他任务,从而导致卡死。在使用中断处理时,确保中断服务程序可以快速执行并及时退出,避免长时间占用CPU资源。 3. Flash操作错误:如果在Flash读写过程中发生错误,例如擦除或编程操作失败,可能会导致系统卡死。可以在Flash操作之前使用软件或硬件的方式检查Flash空闲状态,并及时处理擦除或编程错误。 4. 电源问题:在使用Flash存储器时,确保电源供应稳定可靠。如果电源波动或电源不稳定,可能会导致Flash操作异常,从而导致系统卡死。可以通过添加电源稳压电路或使用更稳定的电源进行解决。 综上所述,当stm32f030f4串口与Flash读写一起使用时出现卡死现象,可以首先检查资源冲突并重新分配资源。同时,确保中断处理程序正确配置和高效执行,检查Flash操作过程中是否存在错误,并确保电源供应的稳定性。如果问题仍然存在,可能需要进一步调试和分析代码以找出具体的问题所在。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值