Dubbo源碼分析之zookeeper註冊中心

1.ZookeeperRegistryFactory

com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistryFactory ,實現 AbstractRegistryFactory 抽象類,Zookeeper Registry 工廠。代碼如下:

public class ZookeeperRegistryFactory extends AbstractRegistryFactory {

    /**
     * Zookeeper 工廠
     */
    private ZookeeperTransporter zookeeperTransporter;

    /**
     * 設置 Zookeeper 工廠
     *
     * 該方法,通過 Dubbo SPI 注入
     *
     * @param zookeeperTransporter Zookeeper 工廠對象
     */
    public void setZookeeperTransporter(ZookeeperTransporter zookeeperTransporter) {
        this.zookeeperTransporter = zookeeperTransporter;
    }

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

}

3. ZookeeperRegistry

com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistry ,實現 FailbackRegistry 抽象類,Zookeeper Registry 。

3.1 屬性 + 構造方法

 1: /**
 2:  * 默認端口
 3:  */
 4: private final static int DEFAULT_ZOOKEEPER_PORT = 2181;
 5: /**
 6:  * 默認 Zookeeper 根節點
 7:  */
 8: private final static String DEFAULT_ROOT = "dubbo";
 9: 
10: /**
11:  * Zookeeper 根節點
12:  */
13: private final String root;
14: /**
15:  * Service 接口全名集合
16:  */
17: private final Set<String> anyServices = new ConcurrentHashSet<String>();
18: /**
19:  * 監聽器集合
20:  */
21: private final ConcurrentMap<URL, ConcurrentMap<NotifyListener, ChildListener>> zkListeners = new ConcurrentHashMap<URL, ConcurrentMap<NotifyListener, ChildListener>>();
22: /**
23:  * Zookeeper 客戶端
24:  */
25: private final ZookeeperClient zkClient;
26: 
27: public ZookeeperRegistry(URL url, ZookeeperTransporter zookeeperTransporter) {
28:     super(url);
29:     if (url.isAnyHost()) {
30:         throw new IllegalStateException("registry address == null");
31:     }
32:     // 獲得 Zookeeper 根節點
33:     String group = url.getParameter(Constants.GROUP_KEY, DEFAULT_ROOT); // `url.parameters.group` 參數值
34:     if (!group.startsWith(Constants.PATH_SEPARATOR)) {
35:         group = Constants.PATH_SEPARATOR + group;
36:     }
37:     this.root = group;
38:     // 創建 Zookeeper Client
39:     zkClient = zookeeperTransporter.connect(url);
40:     // 添加 StateListener 對象。該監聽器,在重連時,調用恢復方法。
41:     zkClient.addStateListener(new StateListener() {
42:         public void stateChanged(int state) {
43:             if (state == RECONNECTED) {
44:                 try {
45:                     recover();
46:                 } catch (Exception e) {
47:                     logger.error(e.getMessage(), e);
48:                 }
49:             }
50:         }
51:     });
52: }
  • root 屬性,Zookeeper 根節點,即首圖的 Root 層。
  • anyServices 屬性,Service 接口接口全名集合。該屬性適可用於監控中心,訂閱整個 Service 層。因爲,Service 層是動態的,可以有不斷有新的 Service 服務發佈(注意,不是服務實例)。在 #doSubscribe(url, notifyListener) 方法中,會更容易理解。
  • zkListeners 屬性,監聽器集合,建立 NotifyListener 和 ChildListener 的映射關係。
  • zkClient 屬性,Zookeeper 客戶端。
  • 構造方法
    • 第 28 至 31 行:設置註冊中心的 URL 。
    • 第 32 至 37 行:設置在 Zookeeper 的根節點,缺省使用 DEFAULT_ROOT 。
    • 第 39 行:調用 ZookeeperTransporter#connect(url) 方法,基於 Dubbo SPI Adaptive 機制,根據 url 參數,加載對應的 ZookeeperTransporter 實現類,創建對應的 ZookeeperClient 實現類的對應。
    • 第 41 至 51 行:添加 StateListener 對象到 ZookeeperClient 對象中。該監聽器,在重連時,在第 45 行的代碼,調用 #recover() 方法,進行恢復邏輯,重新發起註冊和訂閱。

3.2 doRegister

1: @Override
2: protected void doRegister(URL url) {
3:     try {
4:         zkClient.create(toUrlPath(url), url.getParameter(Constants.DYNAMIC_KEY, true));
5:     } catch (Throwable e) {
6:         throw new RpcException("Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
7:     }
8: }
  • 第 4 行:調用 #toUrlPath(url) 方法,獲得 URL 的路徑。
  • 第 4 行:url.parameters.dynamic ,是否動態數據。若爲 false ,該數據爲持久數據,當註冊方退出時,數據依然保存在註冊中心。
  • 第 4 行:調用 ZookeeperClient#create(url, ephemeral) 方法,創建 URL 節點,即我們在首圖看到的 URL 層

3.2.1 toUrlPath

/**
 * 獲得 URL 的路徑
 *
 * Root + Service + Type + URL
 *
 * 被 {@link #doRegister(URL)} 和 {@link #doUnregister(URL)} 調用
 *
 * @param url URL
 * @return 路徑
 */
private String toUrlPath(URL url) {
    return toCategoryPath(url) + Constants.PATH_SEPARATOR + URL.encode(url.toFullString());
}

3.2.2 toCategoryPath

/**
 * 獲得分類路徑
 *
 * Root + Service + Type
 *
 * @param url URL
 * @return 分類路徑
 */
private String toCategoryPath(URL url) {
    return toServicePath(url) + Constants.PATH_SEPARATOR + url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
}

3.2.3 toServicePath

/**
 * 獲得服務路徑
 *
 * Root + Type
 *
 * @param url URL
 * @return 服務路徑
 */
private String toServicePath(URL url) {
    String name = url.getServiceInterface();
    if (Constants.ANY_VALUE.equals(name)) {
        return toRootPath();
    }
    return toRootDir() + URL.encode(name);
}

3.2.4 toRootDir

/**
 * 獲得根目錄
 *
 * Root
 *
 * @return 路徑
 */
private String toRootDir() {
    if (root.equals(Constants.PATH_SEPARATOR)) {
        return root;
    }
    return root + Constants.PATH_SEPARATOR;
}

/**
 * Root
 *
 * @return 根路徑
 */
private String toRootPath() {
    return root;
}

3.3 doUnregister

@Override
protected 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);
    }
}

3.4 doSubscribe

 1: @Override
 2: protected void doSubscribe(final URL url, final NotifyListener listener) {
 3:     try {
 4:         // 處理所有 Service 層的發起訂閱,例如監控中心的訂閱
 5:         if (Constants.ANY_VALUE.equals(url.getServiceInterface())) {
 6:             String root = toRootPath();
 7:             // 獲得 url 對應的監聽器集合
 8:             ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url);
 9:             if (listeners == null) { // 不存在,進行創建
10:                 zkListeners.putIfAbsent(url, new ConcurrentHashMap<NotifyListener, ChildListener>());
11:                 listeners = zkListeners.get(url);
12:             }
13:             // 獲得 ChildListener 對象
14:             ChildListener zkListener = listeners.get(listener);
15:             if (zkListener == null) { // 不存在 ChildListener 對象,進行創建 ChildListener 對象
16:                 listeners.putIfAbsent(listener, new ChildListener() {
17:                     public void childChanged(String parentPath, List<String> currentChilds) {
18:                         for (String child : currentChilds) {
19:                             child = URL.decode(child);
20:                             // 新增 Service 接口全名時(即新增服務),發起該 Service 層的訂閱
21:                             if (!anyServices.contains(child)) {
22:                                 anyServices.add(child);
23:                                 subscribe(url.setPath(child).addParameters(Constants.INTERFACE_KEY, child,
24:                                         Constants.CHECK_KEY, String.valueOf(false)), listener);
25:                             }
26:                         }
27:                     }
28:                 });
29:                 zkListener = listeners.get(listener);
30:             }
31:             // 創建 Service 節點。該節點爲持久節點。
32:             zkClient.create(root, false);
33:             // 向 Zookeeper ,Service 節點,發起訂閱
34:             List<String> services = zkClient.addChildListener(root, zkListener);
35:             // 首次全量數據獲取完成時,循環 Service 接口全名數組,發起該 Service 層的訂閱
36:             if (services != null && !services.isEmpty()) {
37:                 for (String service : services) {
38:                     service = URL.decode(service);
39:                     anyServices.add(service);
40:                     subscribe(url.setPath(service).addParameters(Constants.INTERFACE_KEY, service,
41:                             Constants.CHECK_KEY, String.valueOf(false)), listener);
42:                 }
43:             }
44:         // 處理指定 Service 層的發起訂閱,例如服務消費者的訂閱
45:         } else {
46:             // 子節點數據數組
47:             List<URL> urls = new ArrayList<URL>();
48:             // 循環分類數組
49:             for (String path : toCategoriesPath(url)) {
50:                 // 獲得 url 對應的監聽器集合
51:                 ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url);
52:                 if (listeners == null) { // 不存在,進行創建
53:                     zkListeners.putIfAbsent(url, new ConcurrentHashMap<NotifyListener, ChildListener>());
54:                     listeners = zkListeners.get(url);
55:                 }
56:                 // 獲得 ChildListener 對象
57:                 ChildListener zkListener = listeners.get(listener);
58:                 if (zkListener == null) { // 不存在 ChildListener 對象,進行創建 ChildListener 對象
59:                     listeners.putIfAbsent(listener, new ChildListener() {
60:                         public void childChanged(String parentPath, List<String> currentChilds) {
61:                             // 變更時,調用 `#notify(...)` 方法,回調 NotifyListener
62:                             ZookeeperRegistry.this.notify(url, listener, toUrlsWithEmpty(url, parentPath, currentChilds));
63:                         }
64:                     });
65:                     zkListener = listeners.get(listener);
66:                 }
67:                 // 創建 Type 節點。該節點爲持久節點。
68:                 zkClient.create(path, false);
69:                 // 向 Zookeeper ,PATH 節點,發起訂閱
70:                 List<String> children = zkClient.addChildListener(path, zkListener);
71:                 // 添加到 `urls` 中
72:                 if (children != null) {
73:                     urls.addAll(toUrlsWithEmpty(url, path, children));
74:                 }
75:             }
76:             // 首次全量數據獲取完成時,調用 `#notify(...)` 方法,回調 NotifyListener
77:             notify(url, listener, urls);
78:         }
79:     } catch (Throwable e) {
80:         throw new RpcException("Failed to subscribe " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
81:     }
82: }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章