深度解析dubbo zk客户端实现

本文基于dubbo v2.6.x


一、ZookeeperTransporter

ZookeeperTransporter其实就是使用dubbo spi获取zk客户端的接口
在这里插入图片描述
我们看下它的2个实现类CuratorZookeeperTransporter 与ZkclientZookeeperTransporter
ZkclientZookeeperTransporter
在这里插入图片描述
我们可以看到connect方法实现就是创建ZkclientZookeeperClient 对象
CuratorZookeeperTransporter
在这里插入图片描述
CuratorZookeeperTransporter的connect实现就是创建CuratorZookeeperClient 对象。
配置
如果想使用Zkclient 就可以在xml这样配置:

<dubbo:registry protocol="zookeeper" address="127.0.0.1:2181" client="zkclient" >
</dubbo:registry>

client="zkclient"
如果想使用Curator 就可以在xml这样配置:

  <dubbo:registry protocol="zookeeper" address="127.0.0.1:2181" client="curator" >
  </dubbo:registry>

client="curator"

二、ZookeeperClient

ZookeeperClient接口是dubbo 对zk操作的抽象,这样子做的原因是,java版本的zk客户端有curator与zkClient种,为了能够自由切换,就需要抽象出来一系列操作,分别由不同的客户端来进行实现。这样能够保证系统的灵活性,扩展性,所谓灵活性就是可以根据用户喜好自由配置,扩展性就是以后出现另一种客户端,只需要实现ZookeeperClient接口就可以了。

public interface ZookeeperClient {
    // 创建节点
    void create(String path, boolean ephemeral);
    // 删除节点
    void delete(String path);
    // 获取某个节点的子节点
    List<String> getChildren(String path);
    //为某个节点添加子节点监听器
    List<String> addChildListener(String path, ChildListener listener);
    // 移除某个节点的某个子节点监听器
    void removeChildListener(String path, ChildListener listener);
    // 添加 连接状态监听器
    void addStateListener(StateListener listener);
    // 移除 连接状态监听器
    void removeStateListener(StateListener listener);
    // 是否连接
    boolean isConnected();
    // 关闭
    void close();
    // 获取url
    URL getUrl();

}

上面就是dubbo对zk封装的一系列操作。我们来看下它的继承关系图
在这里插入图片描述
可以看到还是很明显的,接下来的任务就是一一解析了。

三、AbstractZookeeperClient

AbstractZookeeperClient是zk客户端的抽象实现。它做了一些基本的操作,具体的操作还是由子类来实现的。

public abstract class AbstractZookeeperClient<TargetChildListener> implements ZookeeperClient {...}

可以看到AbstractZookeeperClient实现ZookeeperClient 接口,其中TargetChildListener 是个泛型,就跟我们平常写的T一个意思,只不过它这里为了好看使用了TargetChildListener,从字面意思上这个泛型是个childListener监听器。
再来看下它的成员变量

private final URL url;  // 注册中心的url
// 状态listener 列表  CopyOnWriteArraySet:线程安全
private final Set<StateListener> stateListeners = new CopyOnWriteArraySet<StateListener>();
// 缓存着 path与listener的关系
private final ConcurrentMap<String, ConcurrentMap<ChildListener, TargetChildListener>> childListeners = new ConcurrentHashMap<String, ConcurrentMap<ChildListener, TargetChildListener>>();
private volatile boolean closed = false;// 关闭标识 volatile修饰

接下来看下构造,就是设置 注册中心url
在这里插入图片描述
接下来就是create方法,创建某个节点

   @Override
    public void create(String path, boolean ephemeral) {
        if (!ephemeral) {
            if (checkExists(path)) {
                return;
            }
        }
        int i = path.lastIndexOf('/');
        if (i > 0) {
            create(path.substring(0, i), false);
        }
        if (ephemeral) {
            createEphemeral(path);
        } else {
            createPersistent(path);
        }
    }

需要说明的是create 这个方法是一个递归,比如说你一个path是这个样子的(这里为了方便观察我回车换行了)

/dubbo
/com.xuzhaocai.dubbo.provider.IHelloProviderService
/providers
/dubbo%3A%2F%2F192.162.0.174%3A18109%2Fcom.xuzhaocai.dubbo.provider.IHelloProviderService%3Faccesslog%3Dtrue%26anyhost%3Dtrue%26application%3Ddubbo-consumer%26bean.name%3DServiceBean%3Acom.xuzhaocai.dubbo.provider.IHelloProviderService%26dubbo%3D2.0.2%26generic%3Dfalse%26interface%3Dcom.xuzhaocai.dubbo.provider.IHelloProviderService%26methods%3DgetName%26pid%3D47110%26side%3Dprovider%26timestamp%3D1598321369244

它是这样一个执行顺序的,先是判断path是否是个永久节点(ephemeral这个参数表示 是创建永久节点还是临时节点,true的话就是临时节点,false就是永久节点),如果是永久节点的话就需要调用checkExists(path)方法检查这个path是否存在,checkExists是个抽象方法,需要子类来具体实现protected abstract boolean checkExists(String path);,如果存在了就不需要创建了,接着就是从后面查找/这个分割符,如果是存在这个也就是i>0,就会调用create方法,这时候传入path的0位置到i位置那段,而且是个永久节点(注意这就进入递归了),上面那个path第一次 是
在这里插入图片描述
也就是全部,但是创建它需要创建下图这个
在这里插入图片描述
但是要想创建上图这个path就需要创建
在这里插入图片描述
就这样子最后要需要创建dubbo 这个节点(这个步骤里面存在就会返回)
创建的是这个样子的判断是临时节点还是永久节点,临时节点的话就调用createEphemeral(path)创建,永久节点的话就调用createPersistent(path)创建,需要注意的是这两个方法都是抽象方法,需要子类具体实现。

// 创建永久
protected abstract void createPersistent(String path);
// 创建临时
protected abstract void createEphemeral(String path);

接下来再来看下stateListener的两个操作:

// 添加StateListener
@Override
public void addStateListener(StateListener listener) {
    stateListeners.add(listener);
}
// 移除StateListener
@Override
public void removeStateListener(StateListener listener) {
    stateListeners.remove(listener);
}

可以看到很简单,就是操作stateListeners缓存集合进行添加跟移除。
接下来再来看下添加子监听器addChildListener 方法的实现

  @Override
public List<String> addChildListener(String path, final ChildListener listener) {
   
   // 根据path获取 childListener 与 具体节点监听器的对应关系
   ConcurrentMap<ChildListener, TargetChildListener> listeners = childListeners.get(path);
   if (listeners == null) {//不存在创建
       childListeners.putIfAbsent(path, new ConcurrentHashMap<ChildListener, TargetChildListener>());
       listeners = childListeners.get(path);
   }
   // 根据childListener 获取对应的  具体节点监听器
   TargetChildListener targetListener = listeners.get(listener);
   if (targetListener == null) {
       // 没有就创建 目标子类监听器
       listeners.putIfAbsent(listener, createTargetChildListener(path, listener));
       targetListener = listeners.get(listener);
   }
   // 进行添加
   return addTargetChildListener(path, targetListener);
}

在方法中,首先是根据path从childListeners缓存map中获取对应childListener 与 具体节点监听器的对应关系,如果不存在就创建,并且塞进去,接着又是根据childListener获取对应的具体节点监听器,如果不存在就调用createTargetChildListener(path, listener)方法进行创建然后塞到缓存中,最后是调用addTargetChildListener 方法进行添加。其中addTargetChildListener 与 createTargetChildListener 方法都是抽象方法,这里父类只是定义一套执行模版,定义了一套流程。
接着来看看removeChildListener移除方法:

@Override
public void removeChildListener(String path, ChildListener listener) {
    ConcurrentMap<ChildListener, TargetChildListener> listeners = childListeners.get(path);
    if (listeners != null) {
        TargetChildListener targetListener = listeners.remove(listener);
        if (targetListener != null) {
            removeTargetChildListener(path, targetListener);
        }
    }
}

可以看出就是移除对应path 在缓存中的对应ChildListener ,最后调用子类方法removeTargetChildListener 移除具体节点监听器。removeTargetChildListener这个方法也是抽象方法。

 protected abstract TargetChildListener createTargetChildListener(String path, ChildListener listener);
 protected abstract List<String> addTargetChildListener(String path, TargetChildListener listener);
 protected abstract void removeTargetChildListener(String path, TargetChildListener listener);

stateChanged 方法会通知stateListeners集合中所有StateListener状态发生了改变(就是调用循环内对应StateListener 对象的stateChanged方法)

// 状态改变的时候, 进行通知
protected void stateChanged(int state) {
    for (StateListener sessionListener : getSessionListeners()) {
        sessionListener.stateChanged(state);
    }
}

四、ZkclientZookeeperClient

ZkclientZookeeperClient 这类就是zkClient的具体实现了,其实它还不是直接操作的zkClient,有一个成员变量ZkClientWrapper 是对zkClient一层包装,而它只是操作的这个wrapper类。

public class ZkclientZookeeperClient extends AbstractZookeeperClient<IZkChildListener> {....}

ZkclientZookeeperClient继承AbstractZookeeperClient抽象类,那个节点监听是IZkChildListener

private final ZkClientWrapper client;
private volatile KeeperState state = KeeperState.SyncConnected;

在成员变量中client 就是上面说的zkClient的包装类对象,state 是zk连接的一个状态
看下构造方法:

public ZkclientZookeeperClient(URL url) {
      super(url);
      // 创建ZkClientWrapper对象
      client = new ZkClientWrapper(url.getBackupAddress(), 30000);
      client.addListener(new IZkStateListener() {
          @Override
          public void handleStateChanged(KeeperState state) throws Exception {
              ZkclientZookeeperClient.this.state = state;
              if (state == KeeperState.Disconnected) {// 断线状态
                  stateChanged(StateListener.DISCONNECTED);
              } else if (state == KeeperState.SyncConnected) {// 连接状态
                  stateChanged(StateListener.CONNECTED);
              }
          }
          // 创建一个新会话的时候
          @Override
          public void handleNewSession() throws Exception {
              stateChanged(StateListener.RECONNECTED);
          }
      });
      client.start();
  }

在构造中先是调用父类的构造,然后创建ZkClientWrapper 包装类对象,添加一个zk连接状态监听器,最后调用zkClient包装类的start方法启动,在这个状态监听器中实现类两个方法handleStateChanged 与handleNewSession。
当创建一个新的会话的时候就会调用handleNewSession 方法,该方法内调用了父类stateChanged方法来进行状态变动的通知。
当连接状态发生变动的时候就会调用handleStateChanged 方法,该方法先是对state成员做了变更,接着就是判断状态,将zk状态编码转成dubbo zk 状态的编码,并且调用stateChanged方法进行状态变动通知。
在这里插入图片描述
接着来看看创建节点与删除节点方法:

//创建永久节点
@Override
public void createPersistent(String path) {
    try {
        client.createPersistent(path);
    } catch (ZkNodeExistsException e) {
    }
}
// 创建临时节点
@Override
public void createEphemeral(String path) {
    try {
        client.createEphemeral(path);
    } catch (ZkNodeExistsException e) {
    }
}
// 删除节点
@Override
public void delete(String path) {
    try {
        client.delete(path);
    } catch (ZkNoNodeException e) {
    }
}

这个没啥特殊的,就是调用zkClient包装类的对应方法。
获取某个节点的子节点:

@Override
public List<String> getChildren(String path) {
    try {
        return client.getChildren(path);
    } catch (ZkNoNodeException e) {
        return null;
    }
}

检查某个节点是否存在:

@Override
public boolean checkExists(String path) {
    try {
        return client.exists(path);
    } catch (Throwable t) {
    }
    return false;
}

我们重点来看下节点监听器操作的方法:

@Override
public IZkChildListener createTargetChildListener(String path, final ChildListener listener) {
    return new IZkChildListener() {
        @Override
        public void handleChildChange(String parentPath, List<String> currentChilds)// 子类发生变动的时候
                throws Exception {
            listener.childChanged(parentPath, currentChilds);
        }
    };
}

这里就是创建一个IZkChildListener实现对象返回,当节点变动的的时候就会调用handleChildChange 方法,在这个实现方法是调用了ChildListener的childChanged方法来通知节点变动第一个参数是path,第二个就是当前子节点列表。

@Override
public List<String> addTargetChildListener(String path, final IZkChildListener listener) {
    return client.subscribeChildChanges(path, listener);
}
@Override
public void removeTargetChildListener(String path, IZkChildListener listener) {
    client.unsubscribeChildChanges(path, listener);
}

添加与移除节点监听器 方法其实里面就是调用客户端subscribeChildChanges与unsubscribeChildChanges方法。

五、ZkClientWrapper

上面说过ZkClientWrapper是zkClient的一层包装。它主要是对zk连接,添加连接监听器做了异步封装,其他对节点删除,创建,订阅等操作直接调用zkClient的对应方法。
我们先来看下它的成员变量:

 private long timeout;// 超时时间
 private ZkClient client;// zkClient
 private volatile KeeperState state;// 状态
 private ListenableFutureTask<ZkClient> listenableFutureTask;// 异步future
 private volatile boolean started = false;// 启动状态的记录

再来看下构造方法:

public ZkClientWrapper(final String serverAddr, long timeout) {
    this.timeout = timeout;// 超时时间 30000
    listenableFutureTask = ListenableFutureTask.create(new Callable<ZkClient>() {
        @Override
        public ZkClient call() throws Exception {
            return new ZkClient(serverAddr, Integer.MAX_VALUE);
        }
    });
}

timeout超时时间是30s,使用ListenableFutureTask的create方法创建ListenableFutureTask对象,可以看出参数是个Callable实现类,对应call方法中就是创建一个ZkClient对象,这个ZkClient就是zk客户端,它这里面设置了一个超时时间是Integer.MAX_VALUE,这个先不管。
ListenableFutureTask其实是dubbo common项目concurrent包中的FutureTask,它主要是实现了监听功能,我们先当一个黑盒用它。
再来看看start方法:

public void start() {
   if (!started) {
       Thread connectThread = new Thread(listenableFutureTask);
       connectThread.setName("DubboZkclientConnector");
       connectThread.setDaemon(true);
       connectThread.start();
       try {
           client = listenableFutureTask.get(timeout, TimeUnit.MILLISECONDS);
       } catch (Throwable t) {
           logger.error("Timeout! zookeeper server can not be connected in : " + timeout + "ms!", t);
       }
       started = true;
   } else {
       logger.warn("Zkclient has already been started!");
   }
}

就是判断如果没有启动过,就创建一个线程,将listenableFutureTask扔到线程中执行,然后调用get方法获取zkClient对象,listenableFutureTask.get(timeout, TimeUnit.MILLISECONDS);最后将启动状态设置成true。
再来看看addListener添加监听器方法实现:
在这里插入图片描述
添加监听listener其实就是往listenableFutureTask 中添加一个任务(Runnable实现类)。在run方法中获取对应client,然后调用client的subscribeStateChanges方法订阅状态监听。
接下来就看看对节点操作,订阅方法吧:
在这里插入图片描述
在这里插入图片描述
这些方法没啥说的,都是直接调用zkClient里面的方法。

六、CuratorZookeeperClient

CuratorZookeeperClient是对curator操作zk的封装,也是dubbo操作zk客户端默认选择,curator里面其实封装了好多东西,我曾用它做过分布式id,其中提供的api也很好用,是现在操作zk的主流。

public class CuratorZookeeperClient extends AbstractZookeeperClient<CuratorWatcher> {...}

CuratorZookeeperClient 继承AbstractZookeeperClient抽象类,然后它的节点监视器类型是CuratorWatcher。
看下构造方法实现:
在这里插入图片描述
在构造中,先是创建一个builder,设置了连接信息(ip+port),超时时间是5s,重试策略。接着又是权限信息的设置,然后通过builder的build方法获取client(这里使用了建造者模式),接着就是添加状态监视器,当状态变动的时候就会调用stateChanged方法,在stateChanged方法实现中,其实就是做了curator状态与dubbo zk 状态的转变,然后调用CuratorZookeeperClient的stateChanged方法进行状态变动通知,最后调用start方法启动。
接下来看下添加节点与删除节点操作:
在这里插入图片描述
都是直接调用curator客户端的api操作。
接下来看下createTargetChildListener创建节点监听器方法实现:

@Override
public CuratorWatcher createTargetChildListener(String path, ChildListener listener) {
    return new CuratorWatcherImpl(listener);
}

可以看到创建了CuratorWatcherImpl对象返回,这个CuratorWatcherImpl其实是CuratorWatcher的实现。
在这里插入图片描述
当节点监听器监听的那个节点的子节点发生变化的时候,就会调用对应的process 方法,先是判断listener是否是null,获取监听的path,然后调用listener的childChanged方法进行变动通知。
再来看下addTargetChildListener添加节点监听器方法实现:
在这里插入图片描述
removeTargetChildListener移除节点监听器方法:
在这里插入图片描述
这里直接调用了节点监听器的unwatch方法,这个unwatch其实就是把ChildListener的链接制成null,这样变动的时候就不会调用ChildListener的childChanged方法进行通知了,而没有直接像zkClient那样remove

©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页