1. 程式人生 > 其它 >java.net.ConnectException: Connection timed out when connecting to ldap

java.net.ConnectException: Connection timed out when connecting to ldap

背景

請求某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)
{
   
}

測試

  1. 超時值設定21秒內,確實會在指定的時間內丟擲異常 (設定起作用了!)
  2. 超時值設定大於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解析問題,做法是:

  1. 使用nslookup hostname得到ip
  2. 確保這些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);
} 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)

看到有人也有類似的問題:https://stackoverflow.com/questions/56146261/i-set-the-timeout-on-the-socket-and-i-found-that-this-value-cannot-be-greater-t
回答是OS限制,OS中的TCP的超時重試次數決定的。

也就是說TCP在建立連線時候出錯了,超過了OS設定的重試次數,丟擲的TCP Connect Timeout。

甚至有人認為這是一個bug提交給OpenJDK: https://bugs.openjdk.java.net/browse/JDK-7116990

如果需要修改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