问题
最近使用AD域LDAP通信碰到一个TCP底层问题,一个链接第一次点击失败,再点击就正常;
过一段时间又会出现这一情况;
跟踪日志发现:The connection is not established
定位问题:连接是有的,但是没有保活(自行百度)
建立连接后,过了很久连接中断,但是客户端没有收到服务端的信息,认为是有效连接,尝试连接时会报错
解决办法
1 客户端和服务端设置保活
理论上,下一个心跳周期前连接断了,还是会有这个问题
//TCP保活 超时时间
options.setUseKeepAlive(true);
options.setConnectTimeoutMillis(CONNECT_TIMEOUT);
options.setResponseTimeoutMillis(RESPONSE_TIMEOUT);
connection = new LDAPConnection(sslSocketFactory,
options,
adIpAddress,
adDef.getAdPort(),
adminDN,
adDef.getAdAdminPassword());
connectionPool = new LDAPConnectionPool(connection, adDef.getAdMaxPoolCount());
2 断线重连机制(根本解决)
在发起业务请求前,先发起一个请求检测是否有效,
如果无效会抛出异常,catch异常尝试重连 ,如果超过重试次数抛出异常
//发请求前,随意发起一个状态请求(任何内容,尽可能小),如果抛异常则尝试重连
checkAndReconnect(ldapConnection);
//发起真正的业务请求
modResult = ldapConnection.modify(adUser.getUserDN(), mods);
/**
* 检查连接并重试
* @param connection LDAP连接
*/
private static void checkAndReconnect(LDAPConnection connection){
Preconditions.checkNotNull(connection, LOG_PREFIX+"空连接,请检查网络");
try {
connection.getRootDSE();
} catch (LDAPException e) {
int i;
for (i = 0; i < RECONNECT_TIMES; i++) {
try {
connection.reconnect();
log.info(LOG_PREFIX+"重连..{} 成功",i+1);
return;
} catch (LDAPException e1) {
log.info(LOG_PREFIX+"重连..{} 失败{}",i+1,e.getResultString());
}
}
throw Exceptions.newBusinessException(LOG_PREFIX+"重连"+i+"次失败,网络可能已经断开");
}
}
测试
模拟网络断开
Windows防火墙模拟网络断开
开发机是windows,使用防火墙屏蔽端口,出战规则屏蔽端口,启动规则,模拟连接突然断开
Linux防火墙模拟网络断开
//打开防火墙配置文件
vim /etc/sysconfig/iptables
//iptables添加屏蔽规则
-A OUTPUT -p tcp --dport 23000 -j DROP
//重启iptables使规则生效
systemctl restart iptables
//telnet模拟防火墙是否生效
telnet 192.168.1.17 23000
测试成功(中间开启屏蔽规则,放开后连接成功)
心得体会
-
不能通过状态检测来确认连接是否正常
比如不能以isConnected()的结果作为是否连接的标准,它只是一个结果
连接本质上是一个对象,所谓的状态只是一个成员变量,可能网络断了,但状态还是之前正常连接的状态
-
通过尝试连接是否抛异常来判断连接状态及尝试重连
所以简单来说,就是要看方法是否抛异常,如果抛异常,那么它底层的确是去连接了,所以才知道状态
- 通过防火墙模拟网络连接
- debug模式可以在需要的时候才打断点,不需要提前打断点