為了說(shuō)明問(wèn)題, 這里描述一下場(chǎng)景.
應(yīng)用配置的ZK地址是zk.infuq.com, 通過(guò)DNS解析的IP是192.168.0.1, 因此應(yīng)用連接到了ZK1
然后把DNS的映射關(guān)系改成如下圖所示,讓zk.infuq.com解析成ZK2的IP(192.168.0.2), 先關(guān)閉ZK1的服務(wù)(或者禁用2181端口的出入流量)過(guò)了1分鐘再開(kāi)啟服務(wù)(目的就是讓ZK1和應(yīng)用斷開(kāi)連接),根據(jù)應(yīng)用(Dubbo應(yīng)用)的重連機(jī)制, 最后應(yīng)用連接注冊(cè)到ZK2上.
然而, 這樣操作之后, 應(yīng)用真的可以連接到ZK2上嗎?
先說(shuō)下答案, <font color="red">根據(jù)應(yīng)用服務(wù)器配置的zookeeper版本不同,應(yīng)用服務(wù)器可能還會(huì)連接到ZK1上,也可能會(huì)連接到ZK2上. </font>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.9</version>
</dependency>
如果使用的版本是3.4.9, 那么應(yīng)用服務(wù)器會(huì)連接到ZK1上.
Dubbo服務(wù)在啟動(dòng)的過(guò)程中,會(huì)連接ZK,其中會(huì)進(jìn)入org.apache.zookeeper.client.StaticHostProvider#StaticHostProvider代碼,實(shí)例化StaticHostProvider.
public StaticHostProvider(Collection<InetSocketAddress> serverAddresses) throws UnknownHostException {
for (InetSocketAddress address : serverAddresses) {
// ia == null
InetAddress ia = address.getAddress();
// 解析IP
InetAddress resolvedAddresses[] = InetAddress.getAllByName((ia!=null) ? ia.getHostAddress(): address.getHostName());
for (InetAddress resolvedAddress : resolvedAddresses) {
if (resolvedAddress.toString().startsWith("/")
&& resolvedAddress.getAddress() != null) {
this.serverAddresses.add(
new InetSocketAddress(InetAddress.getByAddress(
address.getHostName(),
resolvedAddress.getAddress()),
address.getPort()));
} else {
this.serverAddresses.add(new InetSocketAddress(resolvedAddress.getHostAddress(), address.getPort()));
}
}
}
if (this.serverAddresses.isEmpty()) {
throw new IllegalArgumentException(
"A HostProvider may not be empty!");
}
Collections.shuffle(this.serverAddresses);
}
它會(huì)根據(jù)域名解析IP, 拿到配置的zk.infuq.com域名,解析IP(192.168.0.1).
解析出來(lái)的IP(192.168.0.1)最后封裝成InetSocketAddress并放到serverAddresses集合中.
也就是說(shuō),不管是首次連接ZK還是重連ZK,都是從serverAddresses集合中取出地址進(jìn)行連接ZK,而不會(huì)再重新解析IP.
如果是3.4.13版本
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.13</version>
</dependency>
這個(gè)版本的邏輯是,如果需要連接ZK,都是調(diào)用下面的next方法獲取地址. 而它每次都會(huì)解析IP,一旦DNS有變動(dòng),那么就會(huì)解析到新的IP地址.
public InetSocketAddress next(long spinDelay) {
...
InetSocketAddress curAddr = serverAddresses.get(currentIndex);
try {
// curHostString是域名,例如zk.infuq.com
String curHostString = getHostString(curAddr);
// 解析IP
List<InetAddress> resolvedAddresses = new ArrayList<InetAddress>(Arrays.asList(this.resolver.getAllByName(curHostString)));
if (resolvedAddresses.isEmpty()) {
return curAddr;
}
Collections.shuffle(resolvedAddresses);
// 返回地址
return new InetSocketAddress(resolvedAddresses.get(0), curAddr.getPort());
} catch (UnknownHostException e) {
return curAddr;
}
}
所以,最后結(jié)論如下圖所示,根據(jù)不同的Zookeeper版本,應(yīng)用會(huì)連接不同的ZK.