解决 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();
}
相关推荐
tan180°14 分钟前
MySQL表的操作(3)
linux·数据库·c++·vscode·后端·mysql
优创学社21 小时前
基于springboot的社区生鲜团购系统
java·spring boot·后端
why技术1 小时前
Stack Overflow,轰然倒下!
前端·人工智能·后端
幽络源小助理1 小时前
SpringBoot基于Mysql的商业辅助决策系统设计与实现
java·vue.js·spring boot·后端·mysql·spring
ai小鬼头3 小时前
AIStarter如何助力用户与创作者?Stable Diffusion一键管理教程!
后端·架构·github
G皮T3 小时前
【Elasticsearch】自定义评分检索
大数据·elasticsearch·搜索引擎·查询·检索·自定义评分·_score
简佐义的博客3 小时前
破解非模式物种GO/KEGG注释难题
开发语言·数据库·后端·oracle·golang
Code blocks3 小时前
使用Jenkins完成springboot项目快速更新
java·运维·spring boot·后端·jenkins
追逐时光者4 小时前
一款开源免费、通用的 WPF 主题控件包
后端·.net