解决 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();
}
相关推荐
烛阴23 分钟前
bignumber.js深度解析:驾驭任意精度计算的终极武器
前端·javascript·后端
服务端技术栈42 分钟前
电商营销系统中的幂等性设计:从抽奖积分发放谈起
后端
你的人类朋友1 小时前
✍️Node.js CMS框架概述:Directus与Strapi详解
javascript·后端·node.js
面朝大海,春不暖,花不开1 小时前
自定义Spring Boot Starter的全面指南
java·spring boot·后端
钡铼技术ARM工业边缘计算机2 小时前
【成本降40%·性能翻倍】RK3588边缘控制器在安防联动系统的升级路径
后端
CryptoPP3 小时前
使用WebSocket实时获取印度股票数据源(无调用次数限制)实战
后端·python·websocket·网络协议·区块链
白宇横流学长3 小时前
基于SpringBoot实现的大创管理系统设计与实现【源码+文档】
java·spring boot·后端
草捏子3 小时前
状态机设计:比if-else优雅100倍的设计
后端
考虑考虑5 小时前
Springboot3.5.x结构化日志新属性
spring boot·后端·spring
涡能增压发动积5 小时前
一起来学 Langgraph [第三节]
后端