文章目录
- show me code
- 缘起
- 原因分析
- 几点建议
结论:实际你的配置是生效的,只不过效果不明显而已,通过下面的配置放大直观效果。
show me code
核心代码
public static void main(String[] args) {
RestClientBuilder builder = RestClient.builder("你解析的节点host");
builder.setRequestConfigCallback(ref -> {
ref.setConnectTimeout(100);
ref.setSocketTimeout(100);
ref.setConnectionRequestTimeout(100);
return ref;
});
builder.setHttpClientConfigCallback(ref -> {
// 核心代码
IOReactorConfig build = IOReactorConfig.custom().setSelectInterval(100).build();
ref.setDefaultIOReactorConfig(build);
return ref;
});
RestHighLevelClient restHighLevelClient = new RestHighLevelClient(builder);
}
上面是伪代码但是核心点配置已经注明,先给在ES中底层使用HttpAsyncClientBuilder ,这和以往我们使用传统ApaceHttpClient 不同的是底层使用了NIO:IOReactor,因此在超时时间策略的控制上和传统阻塞方式不一样,有兴趣请继续往下看笔者分析
缘起
由于最近发现ES组件超时频繁,很多甚至出现10几秒到20多秒,然后检查相关配置发现历史同学接入改组件并没有去设置对应的超时时间,导致走到了ES默认的超时时间30s,这个可以在ElasticsearchRestClientProperties
得出结论, 而且还知道连接超时默认1s
调整看起来好像就是注入对应的spring配置,比如我设置如下
spring.elasticsearch.rest.read-timeout=5ms
然后请求测试发现根本没效果,要知道我是拿云主机去连接es,我人在北京机房在上海,别告诉我5ms以内就能处理,【事实上你改到1ms都不一定有效果】,为啥说不一定我前后测过几十次,就有1-2次成功,我还想这玩意还有不确定因素?
成功验证超时堆栈信息如下:
Caused by: java.net.SocketTimeoutException: 5 milliseconds timeout on connection http-outgoing-7 [ACTIVE]
at org.apache.http.nio.protocol.HttpAsyncRequestExecutor.timeout(HttpAsyncRequestExecutor.java:387)
at org.apache.http.impl.nio.client.InternalIODispatch.onTimeout(InternalIODispatch.java:92)
at org.apache.http.impl.nio.client.InternalIODispatch.onTimeout(InternalIODispatch.java:39)
at org.apache.http.impl.nio.reactor.AbstractIODispatch.timeout(AbstractIODispatch.java:175)
at org.apache.http.impl.nio.reactor.BaseIOReactor.sessionTimedOut(BaseIOReactor.java:261)
at org.apache.http.impl.nio.reactor.AbstractIOReactor.timeoutCheck(AbstractIOReactor.java:502)
at org.apache.http.impl.nio.reactor.BaseIOReactor.validate(BaseIOReactor.java:211)
at org.apache.http.impl.nio.reactor.AbstractIOReactor.execute(AbstractIOReactor.java:280)
at org.apache.http.impl.nio.reactor.BaseIOReactor.execute(BaseIOReactor.java:104)
at org.apache.http.impl.nio.reactor.AbstractMultiworkerIOReactor$Worker.run(AbstractMultiworkerIOReactor.java:591)
… 1 common frames omitted
原因分析
其实我一开始先去官网搜了下,发现毛都没有,唯一和timeout相关居然是在创建索引、mapping的request 每次携带参数timeout,如下配置:
CreateIndexRequest createIndexRequest = new CreateIndexRequest();
Settings build = Settings.builder()
.put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 2)
.put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 1).build();
createIndexRequest.timeout("5ms");
createIndexRequest.settings(build);
createIndexRequest.index(xxxx);
return client.indices().create(createIndexRequest, RequestOptions.DEFAULT).isAcknowledged();
但是该参数无法再查询中设置,而且这个timeout控制的是不是客户端到服务端,而是es集群中协调节点 到 副本节点之间的超时时间,很显然不符合我们的要求【默认也是30s】
我想难道在RestHighLevelClient中使用requestConfig注入timeout方式难道不行吗?既然不行为啥提供这种方式,岂不是自相矛盾,直到我偶然看到几次成功,才尝试跟踪上面的堆栈信息 来到timeoutCheck才发现问题所在,在ES底层使用了HttpAsyncClientBuilder#AbstractIOReactor ,NIO在发起请求后通过周期性不断问询服务端selectKey结果,同时做session校验,下面就是关于timeout的校验,
可以看出只有在timeoutCheckInterval
周期内才会去校验,默认1s,这样偶而手动请求就会被跳过,造成大家以为没有实现【后来我通过jmeter方式并发去请求,哪怕是1s 也基本能复现了】,因此通过在构建RestHighLevelClient 中指定NIO的配置如下,适当增加周期检测频率以此来直观处理业务上的超时请求
builder.setHttpClientConfigCallback(ref -> {
// 核心代码
IOReactorConfig build = IOReactorConfig.custom().setSelectInterval(100).build();
ref.setDefaultIOReactorConfig(build);
return ref;
});
几点建议
上面的配置需要注意,如果你的服务类似于B端服务QPS较低,但是业务繁琐,强烈建议调低setSelectInterval,我这里设置100ms ,因为业务低的服务很难被默认1s的周期检查从而进一步探查到超时现象,这样能让业务更明显感知,否则b端业务对es聚合操作较大, 如果极端情况拖到30s,很容易让ES集群超负荷。
而对于C端服务这类请求量级高的,稍微降低到500ms足矣。