迁移Zookeeper时Dubbo是否会注册到新的ZK上

2021-03-07  本文已影响0人  书唐瑞

为了说明问题, 这里描述一下场景.

应用配置的ZK地址是zk.infuq.com, 通过DNS解析的IP是192.168.0.1, 因此应用连接到了ZK1

图片.png

然后把DNS的映射关系改成如下图所示,让zk.infuq.com解析成ZK2的IP(192.168.0.2), 先关闭ZK1的服务(或者禁用2181端口的出入流量)过了1分钟再开启服务(目的就是让ZK1和应用断开连接),根据应用(Dubbo应用)的重连机制, 最后应用连接注册到ZK2上.

图片.png

然而, 这样操作之后, 应用真的可以连接到ZK2上吗?

先说下答案, <font color="red">根据应用服务器配置的zookeeper版本不同,应用服务器可能还会连接到ZK1上,也可能会连接到ZK2上. </font>

<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.4.9</version>
</dependency>

如果使用的版本是3.4.9, 那么应用服务器会连接到ZK1上.

Dubbo服务在启动的过程中,会连接ZK,其中会进入org.apache.zookeeper.client.StaticHostProvider#StaticHostProvider代码,实例化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);
}

它会根据域名解析IP, 拿到配置的zk.infuq.com域名,解析IP(192.168.0.1).

解析出来的IP(192.168.0.1)最后封装成InetSocketAddress并放到serverAddresses集合中.

图片.png

也就是说,不管是首次连接ZK还是重连ZK,都是从serverAddresses集合中取出地址进行连接ZK,而不会再重新解析IP.

如果是3.4.13版本

<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.4.13</version>
</dependency>

这个版本的逻辑是,如果需要连接ZK,都是调用下面的next方法获取地址. 而它每次都会解析IP,一旦DNS有变动,那么就会解析到新的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;
    }
}

所以,最后结论如下图所示,根据不同的Zookeeper版本,应用会连接不同的ZK.

图片.png
上一篇下一篇

猜你喜欢

热点阅读