遇到一个jdk建立SSL连接时的坑:Reverse DNS lookup during SSL handshake

为了解决公司内部没有域名服务器时的http跨机房调用服务的问题: 服务部署在多个机房,有多个服务ip.由于没有内部的智能dns,所有业务使用httpclient时不能很方便的切换服务IP.

因此开发了CustomizeDNSHttpClient. 但是在调用https服务时却遇到了问题: 在windows上,程序会挂起10s左右。下面详细分析下这个问题。

问题现象:

当使用域名,如 https://www.baidu.com 调用时速度很快。但当使用IP地址时 ,https://192.168.100.60:20030 程序会挂起10s左右。

问题分析:

程序最终调用jdk的代码是:
    //SocketFactory类的:public abstract Socket createSocket(InetAddress paramInetAddress1, int paramInt1, InetAddress paramInetAddress2, int paramInt2) throws IOException;

     SocketFactory socketfactory = getSSLContext().getSocketFactory();

     ......
     //ip[i] 是Inet4Address类型
     Socket socket =  socketfactory.createSocket(ip[i],port,localAddress,localPort);

发现使用域名调用和直接使用IP调用的不同在于: ip[i] 对象中的holder的getHostName返回值不同。(直接使用IP时holder().getHostName() == null)

InetAddress的getHostName会间接调用InetAddress.getHostFromNameService进行域名反解析,正是这一步很耗时。最终结果也正是这一步耗时严重。

    String getHostName(boolean check) {
        if (holder().getHostName() == null) {
            holder().hostName = InetAddress.getHostFromNameService(this, check);
        }
        return holder().getHostName();
    }

SSLSocketImpl又是什么时候调用 这个耗时的方法的呢? 通过 http://mail-archives.apache.org/mod_mbox/jmeter-dev/201408.mbox/%3CCAH9fUpaW35vJXPAdRHpb7EHP_B=3Aeo-N+EGM01pqLuJTqzWmQ@mail.gmail.com%3E 可以看到调用栈:

 "Thread Group 1-1" prio=6 tid=0x038f3c00 nid=0xd80 runnable [0x03b7f000]
    java.lang.Thread.State: RUNNABLE
         at java.net.Inet4AddressImpl.getHostByAddr(Native Method)
         at java.net.InetAddress$1.getHostByAddr(Unknown Source)
         at java.net.InetAddress.getHostFromNameService(Unknown Source)
         at java.net.InetAddress.getHostName(Unknown Source)
         at java.net.InetAddress.getHostName(Unknown Source)
         at sun.security.ssl.SSLSocketImpl.getHost(Unknown Source)
         - locked <0x1349be48> (a sun.security.ssl.SSLSocketImpl)
         at sun.security.ssl.Handshaker.getHostSE(Unknown Source)
         at sun.security.ssl.ClientHandshaker.getKickstartMessage(UnknownSource)
         at sun.security.ssl.Handshaker.kickstart(Unknown Source)
         at sun.security.ssl.SSLSocketImpl.kickstartHandshake(UnknownSource)
         - locked <0x1349be48> (a sun.security.ssl.SSLSocketImpl)
         at sun.security.ssl.SSLSocketImpl.performInitialHandshake(UnknownSource)
         - locked <0x1349c038> (a java.lang.Object)
         at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)
         at sun.security.ssl.SSLSocketImpl.getSession(Unknown Source)
         at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:91)
         at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:572)
         at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:180)

至此:找到原因,解析办法很简单,保证传给createSocket的方法的InetAddress对象的host不为空就好。并且当使用InetAddress时,慎重使用getHostName方法。

相关参考:google 下: ` Reverse DNS lookup during SSL handshake `能搜到很多 如: http://stackoverflow.com/questions/3193936/how-to-disable-javas-ssl-reverse-dns-lookup

blog comments powered by Disqus