解决 ES Connection reset by peer 异常

一、问题现象

写入到 ES 报 java.io.IOException: Connection reset by peer 异常,数据不太多,几个小时写入一次。

css 复制代码
java.io.IOException: Connection reset by peer
    at org.elasticsearch.client.RestClient.extractAndWrapCause(RestClient.java:828)
    at org.elasticsearch.client.RestClient.performRequest(RestClient.java:248)
    at org.elasticsearch.client.RestClient.performRequest(RestClient.java:251)
    at org.elasticsearch.client.RestClient.performRequest(RestClient.java:235)
    at org.elasticsearch.client.RestHighLevelClient.internalPerformRequest(RestHighLevelClient.java:1514)
    at org.elasticsearch.client.RestHighLevelClient.performRequest(RestHighLevelClient.java:1484)
    at org.elasticsearch.client.RestHighLevelClient.performRequestAndParseEntity(RestHighLevelClient.java:1454)
    at org.elasticsearch.client.RestHighLevelClient.bulk(RestHighLevelClient.java:497)

二、问题分析

1、客户端的 KeepAlive

和 KeepAlive(最小空闲时间)有关,KeepAlive 默认值是 -1,长连接,表示连接永不过期,可循环重复使用。下图就是设置 KeepAlive 的时候获取的默认时间策略,不设置默认 -1 表示持续连接

2、服务端的 KeepAlive

虽然客户端保持了长链接,然而 Linux 服务器 TCP 的 Keepalive 却有着自己的超时时间,可通过命令查看,如下图,可以看到这台服务器被设置的是 600 秒,也就是 10 分钟。

若超过这个时间,且中间客户端没有操作,也即没有与服务端发生一个 TCP 数据交换,服务器就发送一个心跳包,探测下当前链接是否有效,正常情况下会收到对方的包,表示这个连接可用。

不正常情况下,收不到客户端相应,服务端会多次尝试后发送,之后依然收不到客户端响应(因为网络抖动等原因),就会断开并清除 TCP 连接。

而此时客户端还依然认为自己持有的连接是有效的,如果此时正好有涉及 ES 操作的请求来到,带着自认为有效但实际已经失效的连接的去请求服务端的时候就会报抛出此异常。

因此一种解决方案就是设置 KeepAlive-最小空闲时间,这个时间要小于服务器的 Keepalive 时间,超过这个最小时间客户端主动便释放掉这个连接,下次新请求来到从连接池中重新获取,而不是让服务端主动断开连接。

三、解决方案

方案一

在 ES 客户端连接中构造中设置,如设置最小空闲时间 300 秒,超过这个时间,客户端主动释放掉连接,新请求来到重新获取

java 复制代码
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));

RestClientBuilder builder = RestClient.builder(new HttpHost(host, 9200, "http"));
builder.setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() {
    @Override
    public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpAsyncClientBuilder) {
        return httpAsyncClientBuilder.setDefaultCredentialsProvider(credentialsProvider).setKeepAliveStrategy((response, context) -> TimeUnit.SECONDS.toMillis(300));
    }
});

RestHighLevelClient restHighLevelClient = new RestHighLevelClient(builder);

方案二

因为这个异常不是那么频繁,因此也可以在代码中获取客户端的时候 try catch IOException 后,就重新获取客户端连接 1-3 次左右,超过设定次数就失败,这个也是比较保险的

ini 复制代码
RestHighLevelClient client = null;
try {
    client = esConf.getClient();
} catch (IOException e) {
    log.error("IOException", e);
    client = esConf.getClient();
}
相关推荐
瑞码空间2 分钟前
git知识点黄金笔记
笔记·git·elasticsearch
IT_陈寒24 分钟前
React Hooks闭包陷阱差点让我加班到凌晨
前端·人工智能·后端
码事漫谈1 小时前
本体论,彻底懂
后端
创新技术阁1 小时前
FastapiAdmin 系统日志体系与核心配置参数详解
前端·后端·fastapi
掘金者阿豪1 小时前
SQL Server 迁到金仓,真正麻烦的不是数据,而是这些 T-SQL
后端
Kyrie_kk2 小时前
Java--IO--Path文件访问
java·后端
wxwx_bscxy3222 小时前
基于springboot宠物领养系统的设计与实现
数据库·spring boot·后端·spring·宠物
分支预测失败2 小时前
RISC-V AIA 中断架构实战:从 PLIC 到 APLIC 与 IMSIC 的迁移
linux·后端
源代码•宸2 小时前
前置准备:定时微服务背景和现状
开发语言·经验分享·后端·微服务·云原生·架构·golang
我的div丢了肿么办2 小时前
go语言中的map,map定义不同的数据类型,循环map,map的无序性
后端·go