前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Elasticsearch High Level Rest Client偶现访问集群超时的问题定位与解决

Elasticsearch High Level Rest Client偶现访问集群超时的问题定位与解决

原创
作者头像
bellen
发布2022-02-14 17:04:31
7.6K0
发布2022-02-14 17:04:31
举报

Elasticsearch High Level Rest Client偶现访问集群超时的问题定位与解决

背景

某个客户计划使用云上的es集群,在前期准备工作做完之后,在某天半夜进行切割,切割之后的几个小时内,客户反馈客户端访问ES集群会出现Connection reset by peer 或者 listener timeout after waiting for 30000 ms。

问题定位

客户端使用的是es的High Level Rest Client, es集群和客户端版本都是6.8, 客户反馈在切割之前没有问题,切割之后才出现了超时。客户觉得这一定跟es集群有关系。通过一番排查,发现集群的cpu使用率、load都比较低,不可能因为集群负载高而出现超时。并且,connection reset by peer 这种错误,是因为tcp连接被对端关闭才触发的,所以可能是es服务端主动关闭了连接?

之后在es集群的所有的节点上部署抓包,因为节点比较多,每个节点上的流量也比较大,计划对每个节点抓2个小时的包,看看能不能有什么发现?使用Wireshark对抓包得到pcap文件进行分析,发现抓到的tcp报文都是正常的,没什么异常,这就比较奇怪了。

因为es集群对客户只暴露了一个vip, 这个vip是通过vpc gateway实现的负载均衡功能,因此联系了网络侧的同事,协助排查这个问题。网络侧的同事反馈这种客户端报的connection reset by peer 错误,只可能是后端RS也就是es集群的节点返回的,作为一个网关,vpc gateway是不会主动返回给客户端RST回包的,建议我们在es集群的所有节点上部署抓RST回包,来进行确认。

因此,在所有的es集群的节点上重新部署抓包,只抓RST回包:

代码语言:txt
复制
tcpdump -i eth0  host x.x.x.x and port 9200 and 'tcp[tcpflags] & (tcp-rst) != 0' -vvv -w 1_rst.pcap

跑了一晚上,终于抓到了RST回包:

从上图中可以看到,某一时刻es节点开始对网关ip进行了tcp keepalive的探活,在连续发送了9次的探活报文没有得到响应后直接回复了RST报文给网关,从而断掉了tcp连接。而对于这种回包,网络侧的同事表示这是不正常的,因为客户端的请求链路是客户端->vpc gateway->es节点,而在es节点上看到的tcp报文源ip是vpc gateway的,es节点作为服务端,不应该对直接对网关进行tcp 连接保活的。通过查看es集群节点(centos7)的tcp keepalive配置,发现默认的选项是:

代码语言:txt
复制
net.ipv4.tcp_keepalive_intvl = 75
net.ipv4.tcp_keepalive_probes = 9
net.ipv4.tcp_keepalive_time = 7200

也就是说如果一条tcp连接超过2小时没有流量的话,系统就会主动去探测该条连接进行保活,但是保活的请求直接发送到网关,网关是不会直接回复的,所以可以抓到上面的在连续发送了9次探活报文没有得到响应后直接回复了RST报文给网关,从而断掉了tcp连接。

而客户的业务是在一直运行的,为什么一条连接会有超过2小时没有流量呢?通过查阅es的High Level Rest Client的代码,发现该客户端会使用到client连接池,默认有30个实例,每个client持有一个http连接,并且开启http的keep-alive策略复用连接。但是问题是该客户端是不会对连接进行探测保活的,也就是连接池里可能会存在2小时没有流量的连接;并且客户端也不会主动剔除连接池里实际已经不可用的连接,例如本例中出现的被服务端主动回复RST断掉的连接,因此在客户端如果使用了连接池里已经不可用的连接的时候,会出现connection reset by peer的报错。

而另外一种直接报请求超时的错误又是怎么一回事呢,经过网络侧同事的解释,vpc gateway会有默认的清理过期session的策略,如果一条tcp连接超过2小时没有流量,网关侧是会把这个连接给清理掉的,因为网关也是一个分布式集群,本身的负载和内存容量有限,不能无限制的保持连接。

问题的原因已经清楚了,该如何解决?实际上是需要客户端主动的开启tcp keepalive, 进行连接保活,使得连接池里的不会出现超过2小时没有流量的连接,也使得服务端不会再显式的对与网关之间的tcp请求进行探测保活。而经过google发现es开源社区也有针对类似问题的讨论:在经过网关或者负载均衡器访问集群时,会偶现SocketTimeoutException或者connection reset by peer (https://github.com/elastic/elasticsearch/issues/59261), 而经过讨论后,社区里的研发决定给es的High Level Rest client默认开启tcp keepalive策略来解决这类问题(https://github.com/elastic/elasticsearch/issues/65213),而在实现这个功能之前,临时的解决办法是:

  1. 第一步,在客户端代码中显式的开启tcp keepalive选项: setSoKeepAlive(true)
代码语言:txt
复制
	final RestClientBuilder restClientBuilder = RestClient.builder(/* fill in arguments here */);
    // optionally perform some other configuration of restClientBuilder here if needed
    restClientBuilder.setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder
            /* optionally perform some other configuration of httpClientBuilder here if needed */
            .setDefaultIOReactorConfig(IOReactorConfig.custom()
                    /* optionally perform some other configuration of IOReactorConfig here if needed */
                    .setSoKeepAlive(true)
                    .build()));
    final RestHighLevelClient restHighLevelClient = new RestHighLevelClient(restClientBuilder);
  1. 第二步,设置系统层面的tcp keepalive探测保活时间为300s, 也就是每隔5分钟发送一次探活报文,因为默认的7200s时间太长了,有可能会被网关主动断掉连接。

采用上述临时的解决办法,客户进行了灰度测试,果然不会再出现客户端超时或者connection reset by peer的错误了。

值得一提的是,开源社区里也有人提问是否可以在客户端显式的设置setKeepAliveStrategy,然后把keep-alive时间小一些,比如3分钟:

代码语言:txt
复制
    RestClientBuilder builder = RestClient.builder(httpHosts.toArray(new HttpHost[0]));

    builder.setRequestConfigCallback(
            new RestClientBuilder.RequestConfigCallback() {
                @Override
                public RequestConfig.Builder customizeRequestConfig(RequestConfig.Builder requestConfigBuilder) {
                    return requestConfigBuilder.setConnectTimeout(elasticsearchProperties.getConnectTimeout())
                            .setSocketTimeout(elasticsearchProperties.getSocketTimeout());
                }
            });
    //
    builder.setHttpClientConfigCallback(requestConfig -> requestConfig.setKeepAliveStrategy(
            (response, context) -> TimeUnit.MINUTES.toMillis(3)));
    RestHighLevelClient client = new RestHighLevelClient(builder);

实际上,这种方式和tcp的keepalive没有关系,是指的连接池里http连接复用的最大时间,如果超过这个时间,那么http连接将不再复用,就会被服务端关闭,后续创建新的http连接。这种方式实际上也能够解决上述被网关断开连接的问题,但是因为频繁的创建和关闭连接,效率比较低,会损耗客户端性能,不推荐使用。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Elasticsearch High Level Rest Client偶现访问集群超时的问题定位与解决
    • 背景
      • 问题定位
      相关产品与服务
      Elasticsearch Service
      腾讯云 Elasticsearch Service(ES)是云端全托管海量数据检索分析服务,拥有高性能自研内核,集成X-Pack。ES 支持通过自治索引、存算分离、集群巡检等特性轻松管理集群,也支持免运维、自动弹性、按需使用的 Serverless 模式。使用 ES 您可以高效构建信息检索、日志分析、运维监控等服务,它独特的向量检索还可助您构建基于语义、图像的AI深度应用。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档