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: } |