Dubb源碼解析-Dubbo-Register-ZookeeperRegister

1.ZookeeperRegister模塊簡介

     本文章針對Zookeeper註冊中心,註冊,訂閱源碼設計導讀;至於Zookeeper的優勢,特點我們不做太多講解;

      下圖爲zookeeper存放數據的原理,爲樹狀;因此我們這個模塊本質要做的就是:

  1.  將Zookeeper的數據存放,刪除:Dubbo集成CuratorFramework,客戶端可以對Redis進行連接,刪除,修改,新增節點,添加監聽器等操作,來滿足我們的業務需求;

                       zookeeper

數據結構分析:

  1. dubbo的Root層是根目錄,通過<dubbo:registry group="dubbo" />的“group”來設置zookeeper的根節點,默認是“dubbo”。
  2. Service層是服務接口的全名。
  3. Type層是分類,一共有四種分類,分別是providers(服務提供者列表)、consumers(服務消費者列表)、routes(路由規則列表)、configurations(配置規則列表)。
  4. URL層:根據不同的Type目錄:可以有服務提供者 URL 、服務消費者 URL 、路由規則 URL 、配置規則 URL 。不同的Type關注的URL不同。

ZookeeperRegistry

   這個類繼承FailbackRegistry,在原本的重試機制等下進行了Zoookeeper註冊中心的實現;

基本屬性:

   // 日誌輸出
    private final static Logger logger = LoggerFactory.getLogger(ZookeeperRegistry.class);
    // 默認根節點
    private final static String DEFAULT_ROOT = "dubbo";
    // 就是group
    private final String root;
    // 服務集合
    private final Set<String> anyServices = new ConcurrentHashSet<>();
     // 監聽器集合
    private final ConcurrentMap<URL, ConcurrentMap<NotifyListener, ChildListener>> zkListeners = new ConcurrentHashMap<>();

    // zk客戶端,默認爲CuratorFramework
    private final ZookeeperClient zkClient;

初始化方法:

public ZookeeperRegistry(URL url, ZookeeperTransporter zookeeperTransporter) {
        super(url);
        if (url.isAnyHost()) {
            throw new IllegalStateException("registry address == null");
        }
        // 獲取group信息,默認爲"dubbo"
        String group = url.getParameter(GROUP_KEY, DEFAULT_ROOT);
        // group->/group
        if (!group.startsWith(PATH_SEPARATOR)) {
            // /dubbo開頭
            group = PATH_SEPARATOR + group;
        }
        this.root = group;
        // 客戶端連接
        zkClient = zookeeperTransporter.connect(url);
        // 監聽狀態
        zkClient.addStateListener((state) -> {
            if (state == StateListener.RECONNECTED) {
                logger.warn("Trying to fetch the latest urls, in case there're provider changes during connection loss.\n" +
                        " Since ephemeral ZNode will not get deleted for a connection lose, " +
                        "there's no need to re-register url of this instance.");
                ZookeeperRegistry.this.fetchLatestAddresses();
            } else if (state == StateListener.NEW_SESSION_CREATED) {
                logger.warn("Trying to re-register urls and re-subscribe listeners of this instance to registry...");
                try {
                    ZookeeperRegistry.this.recover();
                } catch (Exception e) {
                    logger.error(e.getMessage(), e);
                }
            } else if (state == StateListener.SESSION_LOST) {
                logger.warn("Url of this instance will be deleted from registry soon. " +
                        "Dubbo client will try to re-register once a new session is created.");
            } else if (state == StateListener.SUSPENDED) {

            } else if (state == StateListener.CONNECTED) {

            }
        });
    }
public boolean isAvailable()存活,public void destroy()銷燬方法
@Override
    public boolean isAvailable() {
        return zkClient.isConnected();
    }

    @Override
    public void destroy() {
        // 調用父類方法
        super.destroy();
        try {
             // 關閉連接
            zkClient.close();
        } catch (Exception e) {
            logger.warn("Failed to close zookeeper client " + getUrl() + ", cause: " + e.getMessage(), e);
        }
    }

註冊,取消註冊方法:調用zookeeper客戶端進行刪除,增加節點

 @Override
    public void doRegister(URL url) {
        try {
            // 創建節點
            zkClient.create(toUrlPath(url), url.getParameter(DYNAMIC_KEY, true));
        } catch (Throwable e) {
            throw new RpcException("Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
        }
    }

    @Override
    public void doUnregister(URL url) {
        try {
            // 刪除節點
            zkClient.delete(toUrlPath(url));
        } catch (Throwable e) {
            throw new RpcException("Failed to unregister " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
        }
    }

訂閱方法:public void doSubscribe(final URL url, final NotifyListener listener)

  @Override
    public void doSubscribe(final URL url, final NotifyListener listener) {
        try {
             // 如果是訂閱所有Service,監控中心就會
            if (ANY_VALUE.equals(url.getServiceInterface())) {
                String root = toRootPath();
                // 獲取url監聽器
                ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url);
                // 初始化監聽器集合
                if (listeners == null) {
                    zkListeners.putIfAbsent(url, new ConcurrentHashMap<>());
                    listeners = zkListeners.get(url);
                }
                // 節點監聽器
                ChildListener zkListener = listeners.get(listener);
                if (zkListener == null) {
                    // 創建監聽器:
                    listeners.putIfAbsent(listener, (parentPath, currentChilds) -> {
                        for (String child : currentChilds) {
                            child = URL.decode(child);
                             // 如果沒有這個節點,添加,然後訂閱
                            if (!anyServices.contains(child)) {
                                anyServices.add(child);
                                subscribe(url.setPath(child).addParameters(INTERFACE_KEY, child,
                                        Constants.CHECK_KEY, String.valueOf(false)), listener);
                            }
                        }
                    });
                    zkListener = listeners.get(listener);
                }
                // 創建serive節點
                zkClient.create(root, false);
                List<String> services = zkClient.addChildListener(root, zkListener);
                if (CollectionUtils.isNotEmpty(services)) {
                    for (String service : services) {
                        service = URL.decode(service);
                        anyServices.add(service);
                        subscribe(url.setPath(service).addParameters(INTERFACE_KEY, service,
                                Constants.CHECK_KEY, String.valueOf(false)), listener);
                    }
                }
            } else {
                List<URL> urls = new ArrayList<>();
                for (String path : toCategoriesPath(url)) {
                    ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url);
                    if (listeners == null) {
                        zkListeners.putIfAbsent(url, new ConcurrentHashMap<>());
                        listeners = zkListeners.get(url);
                    }
                    // 添加監聽器
                    ChildListener zkListener = listeners.get(listener);
                    if (zkListener == null) {
                        listeners.putIfAbsent(listener, (parentPath, currentChilds) -> ZookeeperRegistry.this.notify(url, listener, toUrlsWithEmpty(url, parentPath, currentChilds)));
                        zkListener = listeners.get(listener);
                    }
                    // 創建節點
                    zkClient.create(path, false);
                    List<String> children = zkClient.addChildListener(path, zkListener);
                    if (children != null) {
                        urls.addAll(toUrlsWithEmpty(url, path, children));
                    }
                }
                // 監聽數據變化
                notify(url, listener, urls);
            }
        } catch (Throwable e) {
            throw new RpcException("Failed to subscribe " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
        }
    }

取消訂閱:

@Override
    public void doUnsubscribe(URL url, NotifyListener listener) {
        ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url);
        if (listeners != null) {
           // 調用可客戶端,清除節點監聽器
            ChildListener zkListener = listeners.get(listener);
            if (zkListener != null) {
                if (ANY_VALUE.equals(url.getServiceInterface())) {
                    String root = toRootPath();
                    zkClient.removeChildListener(root, zkListener);
                } else {
                    for (String path : toCategoriesPath(url)) {
                        zkClient.removeChildListener(path, zkListener);
                    }
                }
            }
        }
    }

LookUp

@Override
public List<URL> lookup(URL url) {
    if (url == null) {
        throw new IllegalArgumentException("lookup url == null");
    }
    try {
        List<String> providers = new ArrayList<String>();
        // 遍歷分組類別
        for (String path : toCategoriesPath(url)) {
            // 獲得子節點
            List<String> children = zkClient.getChildren(path);
            if (children != null) {
                providers.addAll(children);
            }
        }
        // 獲得 providers 中,和 consumer 匹配的 URL 數組
        return toUrlsWithoutEmpty(url, providers);
    } catch (Throwable e) {
        throw new RpcException("Failed to lookup " + url + " from zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
    }
}

ZookeeperRegisterFactory

  工廠模式

public class ZookeeperRegistryFactory extends AbstractRegistryFactory {

    private ZookeeperTransporter zookeeperTransporter;

    /**
     * Invisible injection of zookeeper client via IOC/SPI
     * @param zookeeperTransporter
     */
    public void setZookeeperTransporter(ZookeeperTransporter zookeeperTransporter) {
        this.zookeeperTransporter = zookeeperTransporter;
    }

    @Override
    public Registry createRegistry(URL url) {
        return new ZookeeperRegistry(url, zookeeperTransporter);
    }

}

總結:

  • zookeeperRegister實現了Zookeeper註冊中心的註冊,訂閱等等行爲實現;
  • 針對於Zkclient後續通信模塊會講述;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章