1.概述
2.问题描述
使用 Spring boot 服务使用 ES RestHighLevelClient 连接 ES 运行一段时间就会出现Connection reset by peer的异常。这个异常稍微一查就知道,原因是因为 TCP 连接中断,业务数据写入失败。
而连接关闭有很多原因,是 ES 服务器端不能完全控制的。有可能关闭了连接,有可能有防火墙,交换机,vpn 等,也有可能是 keepalive 设置问题,更换了连接服务器节点,网络不稳定等。
3.知识回顾
3.1 TCP 长连接和短连接
TCP 协议中有长连接和短连接之分。短连接在数据包发送完成后就会自己断开,长连接在发包完毕后,会在一定的时间内保持连接,即我们通常所说的 Keepalive(存活定时器)功能。
3.2 TCP 保活机制
保活机制是由一个保活计时器实现的。当计时器被激发,连接一段将发送一个保活探测报文,
另一端接收报文的同时会发送一个 ACK 作为响应。
如果客户端无响应服务器将中断连接,否则会重置保活计时器。
服务器端 Linux 有三个参数可以控制保活时间
tcp_keepalive_time(开启 keepalive 的闲置时长)
tcp_keepalive_intvl(keepalive 探测包的发送间隔)
tcp_keepalive_probes (如果对方不予应答,探测包的发送次数)
TCP 保活机制详见:https://segmentfault.com/a/1190000021057175
4.问题解决
本人实际采用了方式 3 和方式 4
4.1 方案1
方式 1:修改 RestHighLevelClient 连接请求的超时时间
// 默认 1000ms 可以尝试增加到 10000ms
RestClientBuilder builder = RestClient.builder(new HttpHost(endpoint, port))
.setHttpClientConfigCallback(httpClientBuilder->
httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider))
.setRequestConfigCallback(requestConfigBuilder ->
requestConfigBuilder.setConnectTimeout(10000).setSocketTimeout(60000));
return new RestHighLevelClient(builder);
// 单个请求修改:
request.timeout(TimeValue.timeValueSeconds(60));
4.2 方案2
方式 2:创建一个定时器定期探测下 es 保持 keepAlive
// 可以尝试在 Spring boot 创建一个定时器定期探测下 es 保持 keepAlive
@Scheduled(fixedRate = 60000, initialDelay = 60000)
public void keepConnectionAlive() {
log.debug("Trying to ping Elasticsearch");
try {
final long noOfSportsFacilities = restHighLevelClient.status();
log.debug("Ping succeeded for SportsFacilityViewRepository, it contains {} entities", noOfSportsFacilities);
} catch (Exception e) {
log.debug("Ping failed for SportsFacilityViewRepository");
}
}
4.3 方案3
方式 3:设置 RestHighLevelClient keepalive 时间
RestClientBuilder builder = RestClient.builder(hosts);
builder.setHttpClientConfigCallback(callback -> callback.setDefaultCredentialsProvider(credentialsProvider)
.setKeepAliveStrategy((response, context) -> KEEP_ALIVE_MS));
4.4 方案4
方式 4:代码层面捕获异常,重试请求
小结
本文章只是为了简要记录,具体问题的解析可以详见。
https://stackoverflow.com/questions/52997697/how-to-get-around-connection-reset-by-peer-when-using-elasticsearchs-restclie
5.源码
使用RestHighLevelClient请求Es抛错listener timeout after waiting for [30000] ms