解决 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();
}
相关推荐
章豪Mrrey nical5 小时前
前后端分离工作详解Detailed Explanation of Frontend-Backend Separation Work
后端·前端框架·状态模式
派大鑫wink6 小时前
【JAVA学习日志】SpringBoot 参数配置:从基础到实战,解锁灵活配置新姿势
java·spring boot·后端
程序员爱钓鱼7 小时前
Node.js 编程实战:文件读写操作
前端·后端·node.js
xUxIAOrUIII7 小时前
【Spring Boot】控制器Controller方法
java·spring boot·后端
Dolphin_Home7 小时前
从理论到实战:图结构在仓库关联业务中的落地(小白→中级,附完整代码)
java·spring boot·后端·spring cloud·database·广度优先·图搜索算法
zfj3217 小时前
go为什么设计成源码依赖,而不是二进制依赖
开发语言·后端·golang
weixin_462446237 小时前
使用 Go 实现 SSE 流式推送 + 打字机效果(模拟 Coze Chat)
开发语言·后端·golang
JIngJaneIL7 小时前
基于springboot + vue古城景区管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot·后端
小信啊啊8 小时前
Go语言切片slice
开发语言·后端·golang
Victor35610 小时前
Netty(20)如何实现基于Netty的WebSocket服务器?
后端