解决 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();
}
相关推荐
程序员爱钓鱼6 小时前
Go语言实战案例 — 工具开发篇:实现一个图片批量压缩工具
后端·google·go
雁于飞7 小时前
vscode中使用git、githup的基操
笔记·git·vscode·学习·elasticsearch·gitee·github
ChinaRainbowSea8 小时前
7. LangChain4j + 记忆缓存详细说明
java·数据库·redis·后端·缓存·langchain·ai编程
舒一笑8 小时前
同步框架与底层消费机制解决方案梳理
后端·程序员
minh_coo8 小时前
Spring框架事件驱动架构核心注解之@EventListener
java·后端·spring·架构·intellij-idea
今生相伴9919 小时前
ELFK:企业级日志管理的完整解决方案——从入门到精通
运维·elk·elasticsearch
白初&9 小时前
SpringBoot后端基础案例
java·spring boot·后端
计算机学姐12 小时前
基于Python的旅游数据分析可视化系统【2026最新】
vue.js·后端·python·数据分析·django·flask·旅游
该用户已不存在12 小时前
你没有听说过的7个Windows开发必备工具
前端·windows·后端
David爱编程13 小时前
深入 Java synchronized 底层:字节码解析与 MonitorEnter 原理全揭秘
java·后端