背景
请求某ldap服务时候出错,错误如下:
ConnectionTimeout异常,telenet测试
telnet hostname 389
大概需要很久(约2分钟)才能通。
所以想的是设置请求的ConnectionTimeout值大一些试试
代码
增加超时配置
env.put("com.sun.jndi.ldap.connect.timeout", "10000");
来源:https://docs.oracle.com/javase/8/docs/technotes/guides/jndi/jndi-ldap.html
示例代码如下:
try
{
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://hostname:389");
env.put(Context.SECURITY_CREDENTIALS, "simple");
env.put("com.sun.jndi.ldap.connect.timeout", "10000");
InitialLdapContext context = new InitialLdapContext(env, null);
} catch (NamingException e)
{
}
测试
- 超时值设置21秒内,确实会在指定的时间内抛出异常 (设置起作用了!)
- 超时值设置大于21秒,会在21秒左右的样子抛出异常 (设置没起作用?!)
这...
难道是源代码中间有处理吗?我们设置30秒超时,然后顺着new InitialLdapContext(env, null);
这个方法一路跟下去,timeout的值一路传入到socket.connect()
处,再往下就是调用native code了。
可以确定的是,异常抛出的ConnectionTimeout,绝对不是因为我们超了我们设置的超时而抛出的,而是一个内部的io异常导致的。
解决
检索相关关键词,在万能的Stackoverflow上发现同样的问题:https://stackoverflow.com/questions/14100806/java-net-connectexception-connection-timed-out-when-connecting-to-ldap
回答中两个人都提到是DNS解析问题,做法是:
- 使用nslookup hostname得到ip
- 确保这些ip的389端口都是通的
于是按照该方法试了下,多个ip中有一个是好的,修改代码中的ldap url为ldap://ip:389
试了下,果然没有异常出现了。
那为啥Sockect connect timeout是21秒呢?
long start = System.currentTimeMillis();
try {
Socket socket = new Socket();
SocketAddress socketAddress = new InetSocketAddress("220.181.38.148", 389); //baidu ip
socket.connect(socketAddress, 30*1000);
} catch (Exception e) {
long end = System.currentTimeMillis();
System.out.println(end-start);
e.printStackTrace();
}
输出
21071
java.net.ConnectException: Connection timed out: connect
at java.base/java.net.PlainSocketImpl.connect0(Native Method)
at java.base/java.net.PlainSocketImpl.socketConnect(PlainSocketImpl.java:101)
at java.base/java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:399)
at java.base/java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:242)
at java.base/java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:224)
at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
at java.base/java.net.Socket.connect(Socket.java:609)
at java.base/java.net.Socket.connect(Socket.java:558)
查了一下
Stackoverflow:
https://stackoverflow.com/questions/56146261/i-set-the-timeout-on-the-socket-and-i-found-that-this-value-cannot-be-greater-t
https://stackoverflow.com/questions/26896414/where-does-the-socket-timeout-of-21000-ms-come-from
.NET issue:
https://github.com/dotnet/runtime/issues/27232
OpenJDK:
https://bugs.openjdk.java.net/browse/JDK-7116990
可以说是语言无关的操作系统配置问题导致。
简单来说就是TCP建立连接的时候,服务端半天不给响应,那么第一次超时是3秒,第二次是6秒, 第三次是12秒...以此类推。
在Windows操作系统中,通过注册表SetTcpMaxConnectRetransmissions配置,默认是2。也就是会重试2次,3+6+12=21秒。
如果需要修改OS默认的配置,可参考
Linux: http://willbryant.net/overriding_the_default_linux_kernel_20_second_tcp_socket_connect_timeout
Windows: https://docs.microsoft.com/en-US/troubleshoot/windows-client/networking/tcpip-and-nbt-configuration-parameters