scheduled java 失败_@Scheduled和HttpClient的连环坑

Redux实战(Web开发经典丛书)

55.8元

(需用券)

去购买 >

af7eacb10091070e1c2183b367cb2902.png

曾经踩过一个大坑:

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

在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://segmentfault.com/a/1190000013914327

java 11官方入门(第8版)教材

79.84元

包邮

(需用券)

去购买 >

f0f3f55624fb396b1764d42d6df88864.png

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值