资讯专栏INFORMATION COLUMN

Spring中@Scheduled和HttpClient的连环坑

赵连江 / 1813人阅读

摘要:在使用过程中,我们可以使用注解可以方便的实现定时任务。默认单线程经排查后发现,我们使用注解默认的配置的话,所有的任务都是单线程去跑的。参数其实代表了每个路由的最大连接数。怪不得当时在高并发情况下总会出现超时,明明已经设的很高。

前言

本文主要给大家介绍了关于Spring中@Scheduled和HttpClient的坑,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧。

曾经踩过一个大坑:

由于业务特殊性,会定时跑很多定时任务,对业务数据进行补偿操作等。

在Spring使用过程中,我们可以使用@Scheduled注解可以方便的实现定时任务。

有一天早上突然发现,从前一天晚上某一时刻开始,所有的定时任务全部都卡死不再运行了。

@Scheduled默认单线程

经排查后发现,我们使用@Scheduled注解默认的配置的话,所有的任务都是单线程去跑的。写了一个测试的task让它sleep住,就很容易发现,其他所有的task在时间到的时候都没有触发。

如果需要开启多线程处理,则需要进行如下的配置,设置一下线程数:

@Configuration
public class ScheduleConfig implements SchedulingConfigurer {
 @Override
 public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
  taskRegistrar.setScheduler(Executors.newScheduledThreadPool(5));
 }
}

这样就解决了如果一个task卡住,会引起所有task全部卡住的问题。

但是为什么会有task卡住呢?

HttpClient默认参数配置

原来,有些task会定时请求外部服务的restful接口,而HttpClient的配置如下:

PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
  connManager.setMaxTotal(maxConnection);
  httpClient = HttpClients.custom()
    .setConnectionManager(connManager)
    .build();

在最开始使用HttpClient的时候,根本没有想这么多,基本也都是用用默认配置。

追踪源码可以发现,在使用上述方式进行配置的时候,HttpClient的timeout时间竟然全部都是-1,也就是说如果对方服务有问题,HttpClient的请求会永不超时,一直等待。源码如下:

Builder() {
  super();
  this.staleConnectionCheckEnabled = false;
  this.redirectsEnabled = true;
  this.maxRedirects = 50;
  this.relativeRedirectsAllowed = true;
  this.authenticationEnabled = true;
  this.connectionRequestTimeout = -1;
  this.connectTimeout = -1;
  this.socketTimeout = -1;
  this.contentCompressionEnabled = true;
}

所以我们这时候必须手动指定timeout时间,问题就解决了。例如:

PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
  connManager.setMaxTotal(maxConnection);
  RequestConfig defaultRequestConfig = RequestConfig.custom()
.setSocketTimeout(3000)
.setConnectTimeout(3000)
.setConnectionRequestTimeout(3000)
.build();
  httpClient = HttpClients.custom()
.setDefaultRequestConfig(defaultRequestConfig)
.setConnectionManager(connManager)
.build();

联想到另一个问题

其实HttpClient的使用过程中也遇到过另外一个配置的问题,就是defaultMaxPerRoute这个参数。

最开始使用的时候也没有注意过这个参数,只是设置过连接池的最大连接数maxTotal。

defaultMaxPerRoute参数其实代表了每个路由的最大连接数。比如你的系统需要访问另外两个服务:google.com 和 bing.com。如果你的maxTotal设置了100,而defaultMaxPerRoute设置了50,那么你的每一个服务的最大请求数最大只能是50。

那么如果defaultMaxPerRoute没有设置呢,追踪源码:

public PoolingHttpClientConnectionManager(
  final HttpClientConnectionOperator httpClientConnectionOperator,
  final HttpConnectionFactory connFactory,
  final long timeToLive, final TimeUnit tunit) {
  super();
  this.configData = new ConfigData();
  //这里使用的CPool构造方法,第二个参数即为defaultMaxPerRoute,也就是默认为2。
  this.pool = new CPool(new InternalConnectionFactory(
this.configData, connFactory), 2, 20, timeToLive, tunit);
  this.pool.setValidateAfterInactivity(2000);
  this.connectionOperator = Args.notNull(httpClientConnectionOperator, "HttpClientConnectionOperator");
  this.isShutDown = new AtomicBoolean(false);
}

这里发现,原来默认值竟然只有2。怪不得当时在高并发情况下总会出现超时,明明maxTotal已经设的很高。

所以如果你的服务访问很多不同的外部服务,并且并发量比较大,一定要好好配置maxTotal和defaultMaxPerRoute两个参数。

所以后来再使用任何新的东西,都有好好看下都什么配置,有疑问的一定要先查一下,不要网上copy一段代码直接就用。当时可能没问题,但是以后没准就被坑了。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/74399.html

相关文章

  • Springboot定时任务踩记录

    摘要:前言在使用整合定时任务,发现当某个定时任务执行出现执行时间过长的情况时会阻塞其他定时任务的执行。问题定位后续通过翻查的文档以及打印日志输出当前线程信息得知问题是由于默认使用只要个线程处理定时任务。问题复盘需要注意示例的版本为。 前言 在使用Springboot整合定时任务,发现当某个定时任务执行出现执行时间过长的情况时会阻塞其他定时任务的执行。 问题定位 后续通过翻查Springboo...

    liujs 评论0 收藏0
  • Spring之定时任务基本使用篇

    摘要:表示起始时间开始触发,然后每隔固定时间触发一次如在域使用则意味着分钟触发一次,而,等分别触发一次表示列出枚举值值。 showImg(https://segmentfault.com/img/remote/1460000015852353); 文章链接:https://liuyueyi.github.io/hexblog/2018/08/01/180801-Spring之定时任务基本使用...

    Jingbin_ 评论0 收藏0
  • SpringBoot并发定时任务实现、动态定时任务实现(看这一篇就够了)

    摘要:也是自带的一个基于线程池设计的定时任务类。其每个调度任务都会分配到线程池中的一个线程执行,所以其任务是并发执行的,互不影响。 原创不易,如需转载,请注明出处https://www.cnblogs.com/baixianlong/p/10659045.html,否则将追究法律责任!!! 一、在JAVA开发领域,目前可以通过以下几种方式进行定时任务 1、单机部署模式 Timer:jdk中...

    BWrong 评论0 收藏0
  • spring-boot | 多线程并发定时任务

    摘要:多线程并发定时任务刚刚看了下实现定时任务的文章,感觉还不错。存在问题但是后来发现个问题,通过同时测试几个任务发现,所有的任务都是在同一个线程池中的同一个线程来完成的。 spring-boot | 多线程并发定时任务 刚刚看了下Spring Boot实现定时任务的文章,感觉还不错。Spring Boot 使用Spring自带的Schedule来实现定时任务变得非常简单和方便。在这里个大家...

    silenceboy 评论0 收藏0

发表评论

0条评论

赵连江

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<