Java建立Socket慢的問題
在Java編程中,一般都是使用下面的語句來建立Socket
String ip ="192.168.0.100"; int port = 8090; Socket socket = new Socket(ip,port); //....... |
在有些JDK和JRE的版本中,會發(fā)生這個new Socket語句非常緩慢的問題(Linux和windows環(huán)境中都可能有這種問題)。
我遇到這種問題,還是在Java中使用Tomcat,配置了一個數(shù)據(jù)庫連接池,最小連接數(shù)配了10個,在軟件一啟動時,就會建立這些連接,結(jié)果就發(fā)現(xiàn)啟動過程卡在這里了,悲劇了。。。。
更有甚者,在某個Java做得項目中,啟動時,會建立一個長連接的連接池,一啟動時就會建立一些Socket客戶端,在啟動后并發(fā)訪問中來使用,結(jié)果,等啊等啊。。。
(最討厭的是,不知道到底哪些版本出這樣的問題,好像JDK1.5時看到過這個問題,后面的版本也有時候有問題,再有就是IBM的JDK也說不定哪個版本會出這個問題,而且,Linux平臺出現(xiàn)問題的時候更多)
經(jīng)過這兩次,我下決心找一下這個問題,后來發(fā)現(xiàn)網(wǎng)上也有人討論這個問題,但是很少,后來在sun(當(dāng)時Java還是sun的)的bug庫中有個條目在描述這個問題,鏈接找不到了,大體上的描述就是:
Java中,使用字符串的主機和整數(shù)的端口號來構(gòu)造Socket的構(gòu)造函數(shù)是
public Socket(String host, int port) throws UnknownHostException, IOException |
可以明確的是,第一個參數(shù)的含義是host,因此,java將第一個參數(shù)字符串會理解成主機名,因此,在建立Socket時會根據(jù)主機名來查找主機的IP地址。因此,即便實際給的參數(shù)是一個字符串中包含的IP地址,但是,這是Java所無法分辨的。
因為機器的DNS配置不大對頭,到DNS查找主機對應(yīng)IP,消耗了太多的時間,所以,建立連接變得非常緩慢,就造成了前邊所描述的問題。至于怎樣到DNS查找,就不是本文所描述的內(nèi)容了。
如果想通過編程避免這個問題,應(yīng)該怎么處理呢?我們可以看到,Socket還可以這樣建立
public Socket(InetAddress address, int port) throws IOException |
而InetAddress可以通過如下的方法來創(chuàng)建:
public static InetAddress getByName(String host) throws UnknownHostException |
或
public static InetAddress getByAddress(byte[] addr) throws UnknownHostException |
使用字符串的host來創(chuàng)建InetAddress,可以想象,和使用字符串的host來創(chuàng)建Socket一樣會緩慢(因為需要
到DNS),而是要字節(jié)數(shù)組來創(chuàng)建又是如何呢?我們看這個函數(shù)的解釋:
在給定原始 IP 地址的情況下,返回 InetAddress 對象。參數(shù)按網(wǎng)絡(luò)字節(jié)順序:地址的高位字節(jié)位于getAddress()[0] 中。此方法不會阻塞,即不執(zhí)行任何反向名稱服務(wù)查找操作。 IPv4 地址 byte 數(shù)組的長度必須為 4 個字節(jié),IPv6 byte 數(shù)組的長度必須為 16 個字節(jié) |
首先可以明確看到,不執(zhí)行反向名稱服務(wù)查找查找;第二,講IP地址轉(zhuǎn)換成字節(jié)數(shù)組。因為Java看到這個函數(shù)時已經(jīng)知道送來的是IP地址,所以沒有查DNS的問題了。
從這里可以看到,其實這個問題是因為我們在送參數(shù)時,沒有更加準確的去區(qū)分IP地址和主機名這兩個概念。送IP地址和送主機名的場景如果清晰的區(qū)分開來就不會出這種問題了。
一個很糟糕的是Java的JDBC,我記得好像JDBC是建立的Socket上的,而且是字符串方式送的主機或IP地址,所以,這個問題會影響到使用數(shù)據(jù)庫的Java應(yīng)用,如前邊提到的。
再有,如果打算使用字符串這種構(gòu)造地址或Socket的方式,應(yīng)該配置DNS,使之能夠很快的找到主機。說白了,在windows下,你可以在windows\system32\drivers\etc\hosts文件這,加入一行(示例如下):
192.168.0.100 192.168.0.100 |
這樣就告訴系統(tǒng),在尋找主機名“192.168.0.100”時,從這里就返回IP地址是192.168.0.200。這樣就省了很多的時間。對于Linux,是/etc/hosts文件。
結(jié)論:
(1)如果是自己建立TCP連接這類的應(yīng)用,在可能的話,使用getByName這種方式,直接傳IP地址;
(2)修改hosts文件,有些時候能夠修改,有些情況,可能沒有辦法改別人系統(tǒng)的文件;
(3)配置好的DNS