Spring中@Scheduled和HttpClient的连环坑

作者:白色夜空 时间:2023-10-19 23:06:00 

前言

本文主要给大家介绍了关于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<HttpRoute, ManagedHttpClientConnection> 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://segmentfault.com/a/1190000013914327

标签:spring,@scheduled,httpclient
0
投稿

猜你喜欢

  • Java中的zookeeper常用命令详解

    2022-12-07 05:46:35
  • C# 通过反射初探ORM框架的实现原理(详解)

    2023-08-30 20:05:30
  • Java数据结构超详细分析二叉搜索树

    2022-12-01 01:34:20
  • SpringBoot+Elasticsearch实现数据搜索的方法详解

    2023-04-17 02:45:46
  • Android通知栏前台服务的实现

    2022-10-13 22:57:46
  • c#保存窗口位置大小操作类(序列化和文件读写功能)

    2023-07-15 18:51:06
  • jdk15的安装与配置全过程记录

    2023-01-06 05:45:10
  • Java中对象的销毁方法分析

    2023-01-09 16:44:29
  • java判断字符串是否为数字的方法小结

    2023-11-25 05:54:52
  • Android中扫描多媒体文件操作详解

    2022-06-02 09:06:53
  • Java实现将类数据逐行写入CSV文件的方法详解

    2023-02-27 17:11:11
  • springcloud gateway如何实现路由和负载均衡

    2023-01-09 07:13:35
  • Java线程安全的计数器简单实现代码示例

    2023-11-09 15:41:57
  • java判断今天,昨天,前天,不能用秒间隔的简单实例

    2021-07-27 23:01:35
  • springmvc和js前端的数据传递和接收方式(两种)

    2021-06-15 08:33:37
  • 聊聊Redis的单线程模型

    2022-02-21 09:20:42
  • Java并发包线程池ThreadPoolExecutor的实现

    2022-11-10 09:52:41
  • Java开发之内部类对象的创建及hook机制分析

    2023-11-27 04:45:20
  • MyBatis常用标签以及使用技巧总结

    2022-02-27 20:52:14
  • 详解Java实现单例的五种方式

    2021-06-11 03:54:03
  • asp之家 软件编程 m.aspxhome.com