这里会先写触发原因,然后结合我的具体事例,并给出解决方案
首先时触发原因
- 如果一端的Socket被关闭(或主动关闭,或因为异常退出而 引起的关闭),另一端仍发送数据,发送的第一个数据包引发该异常(Connect reset by peer)。
Socket默认连接60秒,60秒之内没有进行心跳交互,即读写数据,就会自动关闭连接。 - 一端退出,但退出时并未关闭该连接,另一端如果在从连接中读数据则抛出该异常(Connection reset)。
简单的说就是在连接断开后的读和写操作引起的。
不同局域网的不同服务器之间调用的时候(这里可以把请求方作为客户端,另一方作为服务端) 两端通信的时候 服务端一方将通道关闭 而另一方客户端未接收关闭的命令 下次一次客户端请求依旧采用该通道,而服务器那边此通道已经关闭,所以导致如此
下面说一下我这里碰到的具体问题,服务中集成使用到Elaticsearch,每天深夜的时候报错connection reset by beer。
具体排查发现是,当搜索模块长时间未被使用时,也就是服务中集成的elaticsearch(客户端)长时间没有去向elaticsearch集群去调用请求,导致之前的通道被单方面关闭,而另一方未收到关闭信息。在这个情况下未收到关闭信息的一方后续调用请求的时候,再次使用之前的通道去请求,就会报这个错误。
解决方案 将客户端的通道保持时间 缩短小于客户端保持时间 keepalive时间
- 先设置好客户端链接的心跳策略
public class CustomConnectionKeepAliveStrategy extends DefaultConnectionKeepAliveStrategy {
public static final CustomConnectionKeepAliveStrategy INSTANCE = new CustomConnectionKeepAliveStrategy();
private CustomConnectionKeepAliveStrategy() {
super();
}
/**
* 最大keep alive的时间(分钟)
* 这里默认为10分钟,可以根据实际情况设置。可以观察客户端机器状态为TIME_WAIT的TCP连接数,如果太多,可以增大此值。
*/
private final long MAX_KEEP_ALIVE_MINUTES = 30;
@Override
public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
long keepAliveDuration = super.getKeepAliveDuration(response, context);
// <0 为无限期keepalive
// 将无限期替换成一个默认的时间
if(keepAliveDuration < 0){
return TimeUnit.MINUTES.toMillis(MAX_KEEP_ALIVE_MINUTES);
}
return keepAliveDuration;
}
}
- 在设置setHttpClientConfigCallback es的客户端配置回调的时候加上心跳策略
/**
* 设置ES客户端连接保活时长
*/
httpClientBuilder.setKeepAliveStrategy(CustomConnectionKeepAliveStrategy.INSTANCE);
至此解决问题。