dubbo-registry分析

從github上git clone下來一份源代碼,可以看到module有這麼多:
- dubbo-common 公共邏輯模塊,包括Util類和通用模型。
- dubbo-remoting 遠程通訊模塊,相當於Dubbo協議的實現,如果RPC用RMI協議則不需要使用此包。
- dubbo-rpc 遠程調用模塊,抽象各種協議,以及動態代理,只包含一對一的調用,不關心集羣的管理。
- dubbo-cluster 集羣模塊,將多個服務提供方僞裝爲一個提供方,包括:負載均衡,
容錯,路由等,集羣的地址列表可以是靜態配置的,也可以是由註冊中心下發。
- dubbo-registry 註冊中心模塊,基於註冊中心下發地址的集羣方式,以及對各種註冊中心的抽象。
- dubbo-monitor 監控模塊,統計服務調用次數,調用時間的,調用鏈跟蹤的服務。
- dubbo-config 配置模塊,是Dubbo對外的API,用戶通過Config使用Dubbo,隱藏Dubbo所有細節。
- dubbo-container容器模塊,是一個Standlone的容器,以簡單的Main加載Spring啓動,因爲服務通常不需要Tomcat/JBoss等Web容器的特性,沒必要用Web容器去加載服務。

用dubbo用了很久,也沒有認真讀其中的細節,打算通過每寫一次博客來記錄自己對registry,rpc,container,remoting,cluster,config,monitor,admin的理解吧

RegistryService和AbstractRegistry

RegistryService接口定定義了註冊何訂閱的5個方法:

  • void register(URL url);
    註冊數據,比如:提供者地址,消費者地址,路由規則,覆蓋規則,等數據。註冊需處理契約:
  • 當URL設置了check=false時,註冊失敗後不報錯,在後臺定時重試,否則拋出異常。
  • 當URL設置了dynamic=false參數,則需持久存儲,否則,當註冊者出現斷電等情況異常退出時,需自動刪除。
  • 當URL設置了category=routers時,表示分類存儲,缺省類別爲providers,可按分類部分通知數據。
  • 當註冊中心重啓,網絡抖動,不能丟失數據,包括斷線自動刪除數據。
  • 允許URI相同但參數不同的URL並存,不能覆蓋。
    example : url 註冊信息,不允許爲空,如:dubbo://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin
    public void register(URL url) {
        if (url == null) {
            throw new IllegalArgumentException("register url == null");
        }
        if (logger.isInfoEnabled()) {
            logger.info("Register: " + url);
        }
        registered.add(url);
    }
  • void unregister(URL url);
    取消註冊.
    取消註冊需處理契約:
  • 如果是dynamic=false的持久存儲數據,找不到註冊數據,則拋IllegalStateException,否則忽略。
  • 按全URL匹配取消註冊。
    example:url 註冊信息,不允許爲空,如:dubbo://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin
    public void unregister(URL url) {
        if (url == null) {
            throw new IllegalArgumentException("unregister url == null");
        }
        if (logger.isInfoEnabled()) {
            logger.info("Unregister: " + url);
        }
        registered.remove(url);
    }
  • void subscribe(URL url, NotifyListener listener);

訂閱符合條件的已註冊數據,當有註冊數據變更時自動推送. 訂閱需處理契約:
- 當URL設置了check=false時,訂閱失敗後不報錯,在後臺定時重試。
- 當URL設置了category=routers,只通知指定分類的數據,多個分類用逗號分隔,並允許星號通配,表示訂閱所有分類數據。
- 允許以interface,group,version,classifier作爲條件查詢,如:interface=com.alibaba.foo.BarService&version=1.0.0
- 並且查詢條件允許星號通配,訂閱所有接口的所有分組的所有版本,或:interface=&group=&version=&classifier=
- 當註冊中心重啓,網絡抖動,需自動恢復訂閱請求。
- 允許URI相同但參數不同的URL並存,不能覆蓋。
- 必須阻塞訂閱過程,等第一次通知完後再返回。
@param url 訂閱條件,不允許爲空,如:consumer://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin
@param listener 變更事件監聽器,不允許爲空

  • void unsubscribe(URL url, NotifyListener listener);
    取消訂閱.取消訂閱需處理契約:
  • 如果沒有訂閱,直接忽略。
  • 按全URL匹配取消訂閱。
    @param url 訂閱條件,不允許爲空,如:consumer://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin
    @param listener 變更事件監聽器,不允許爲空
   public void unsubscribe(URL url, NotifyListener listener) {
        if (url == null) {
            throw new IllegalArgumentException("unsubscribe url == null");
        }
        if (listener == null) {
            throw new IllegalArgumentException("unsubscribe listener == null");
        }
        if (logger.isInfoEnabled()) {
            logger.info("Unsubscribe: " + url);
        }
        Set<NotifyListener> listeners = subscribed.get(url);
        if (listeners != null) {
            listeners.remove(listener);
        }
    }
  • List lookup(URL url);
    查詢符合條件的已註冊數據,與訂閱的推模式相對應,這裏爲拉模式,只返回一次結果。
    @param url 查詢條件,不允許爲空,如:consumer://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin
    @return 已註冊信息列表,可能爲空,含義同{@link com.alibaba.dubbo.registry.NotifyListener#notify(List)}的參數。
    @see com.alibaba.dubbo.registry.NotifyListener#notify(List)
  public List<URL> lookup(URL url) {
        List<URL> result = new ArrayList<URL>();
        Map<String, List<URL>> notifiedUrls = getNotified().get(url);
        if (notifiedUrls != null && notifiedUrls.size() > 0) {
            for (List<URL> urls : notifiedUrls.values()) {
                for (URL u : urls) {
                    if (!Constants.EMPTY_PROTOCOL.equals(u.getProtocol())) {
                        result.add(u);
                    }
                }
            }
        } else {
            final AtomicReference<List<URL>> reference = new AtomicReference<List<URL>>();
            NotifyListener listener = new NotifyListener() {
                public void notify(List<URL> urls) {
                    reference.set(urls);
                }
            };
            subscribe(url, listener); // 訂閱邏輯保證第一次notify後再返回
            List<URL> urls = reference.get();
            if (urls != null && urls.size() > 0) {
                for (URL u : urls) {
                    if (!Constants.EMPTY_PROTOCOL.equals(u.getProtocol())) {
                        result.add(u);
                    }
                }
            }
        }
        return result;
    }
  • AbstractRegistry構造方法
    public AbstractRegistry(URL url) {
        setUrl(url);
        // 啓動文件保存定時器
        syncSaveFile = url.getParameter(Constants.REGISTRY_FILESAVE_SYNC_KEY, false);
        String filename = url.getParameter(Constants.FILE_KEY, System.getProperty("user.home") + "/.dubbo/dubbo-registry-"
                + url.getParameter(Constants.APPLICATION_KEY) + "-" + url.getAddress() + ".cache");
        File file = null;
        if (ConfigUtils.isNotEmpty(filename)) {
            file = new File(filename);
            if (!file.exists() && file.getParentFile() != null && !file.getParentFile().exists()) {
                if (!file.getParentFile().mkdirs()) {
                    throw new IllegalArgumentException("Invalid registry store file " + file + ", cause: Failed to create directory " + file.getParentFile() + "!");
                }
            }
        }
        this.file = file;
        loadProperties();
        notify(url.getBackupUrls());
    }
  • loadProperties,吧本地磁盤cache的文件load到經來
  • notify(url.getBackupUrls()); 通知消費者

    FailbackRegistry

  • register

public void register(URL url) {
        if (destroyed.get()){
            return;
        }
        super.register(url);
        failedRegistered.remove(url);
        failedUnregistered.remove(url);
        try {
            // 向服務器端發送註冊請求
            doRegister(url);
        } catch (Exception e) {
            Throwable t = e;

            // 如果開啓了啓動時檢測,則直接拋出異常
            boolean check = getUrl().getParameter(Constants.CHECK_KEY, true)
                    && url.getParameter(Constants.CHECK_KEY, true)
                    && !Constants.CONSUMER_PROTOCOL.equals(url.getProtocol());
            boolean skipFailback = t instanceof SkipFailbackWrapperException;
            if (check || skipFailback) {
                if (skipFailback) {
                    t = t.getCause();
                }
                throw new IllegalStateException("Failed to register " + url + " to registry " + getUrl().getAddress() + ", cause: " + t.getMessage(), t);
            } else {
                logger.error("Failed to register " + url + ", waiting for retry, cause: " + t.getMessage(), t);
            }

            // 將失敗的註冊請求記錄到失敗列表,定時重試
            failedRegistered.add(url);
        }
    }
  • unregister
public void unregister(URL url) {
        if (destroyed.get()){
            return;
        }
        super.unregister(url);
        failedRegistered.remove(url);
        failedUnregistered.remove(url);
        try {
            // 向服務器端發送取消註冊請求
            doUnregister(url);
        } catch (Exception e) {
            Throwable t = e;

            // 如果開啓了啓動時檢測,則直接拋出異常
            boolean check = getUrl().getParameter(Constants.CHECK_KEY, true)
                    && url.getParameter(Constants.CHECK_KEY, true)
                    && !Constants.CONSUMER_PROTOCOL.equals(url.getProtocol());
            boolean skipFailback = t instanceof SkipFailbackWrapperException;
            if (check || skipFailback) {
                if (skipFailback) {
                    t = t.getCause();
                }
                throw new IllegalStateException("Failed to unregister " + url + " to registry " + getUrl().getAddress() + ", cause: " + t.getMessage(), t);
            } else {
                logger.error("Failed to uregister " + url + ", waiting for retry, cause: " + t.getMessage(), t);
            }

            // 將失敗的取消註冊請求記錄到失敗列表,定時重試
            failedUnregistered.add(url);
        }
    }
  • subscribe
 public void subscribe(URL url, NotifyListener listener) {
        if (destroyed.get()){
            return;
        }
        super.subscribe(url, listener);
        removeFailedSubscribed(url, listener);
        try {
            // 向服務器端發送訂閱請求
            doSubscribe(url, listener);
        } catch (Exception e) {
            Throwable t = e;

            List<URL> urls = getCacheUrls(url);
            if (urls != null && urls.size() > 0) {
                notify(url, listener, urls);
                logger.error("Failed to subscribe " + url + ", Using cached list: " + urls + " from cache file: " + getUrl().getParameter(Constants.FILE_KEY, System.getProperty("user.home") + "/dubbo-registry-" + url.getHost() + ".cache") + ", cause: " + t.getMessage(), t);
            } else {
                // 如果開啓了啓動時檢測,則直接拋出異常
                boolean check = getUrl().getParameter(Constants.CHECK_KEY, true)
                        && url.getParameter(Constants.CHECK_KEY, true);
                boolean skipFailback = t instanceof SkipFailbackWrapperException;
                if (check || skipFailback) {
                    if (skipFailback) {
                        t = t.getCause();
                    }
                    throw new IllegalStateException("Failed to subscribe " + url + ", cause: " + t.getMessage(), t);
                } else {
                    logger.error("Failed to subscribe " + url + ", waiting for retry, cause: " + t.getMessage(), t);
                }
            }

            // 將失敗的訂閱請求記錄到失敗列表,定時重試
            addFailedSubscribed(url, listener);
        }
    }
  • unsubscribe
public void unsubscribe(URL url, NotifyListener listener) {
        if (destroyed.get()){
            return;
        }
        super.unsubscribe(url, listener);
        removeFailedSubscribed(url, listener);
        try {
            // 向服務器端發送取消訂閱請求
            doUnsubscribe(url, listener);
        } catch (Exception e) {
            Throwable t = e;

            // 如果開啓了啓動時檢測,則直接拋出異常
            boolean check = getUrl().getParameter(Constants.CHECK_KEY, true)
                    && url.getParameter(Constants.CHECK_KEY, true);
            boolean skipFailback = t instanceof SkipFailbackWrapperException;
            if (check || skipFailback) {
                if (skipFailback) {
                    t = t.getCause();
                }
                throw new IllegalStateException("Failed to unsubscribe " + url + " to registry " + getUrl().getAddress() + ", cause: " + t.getMessage(), t);
            } else {
                logger.error("Failed to unsubscribe " + url + ", waiting for retry, cause: " + t.getMessage(), t);
            }

            // 將失敗的取消訂閱請求記錄到失敗列表,定時重試
            Set<NotifyListener> listeners = failedUnsubscribed.get(url);
            if (listeners == null) {
                failedUnsubscribed.putIfAbsent(url, new ConcurrentHashSet<NotifyListener>());
                listeners = failedUnsubscribed.get(url);
            }
            listeners.add(listener);
        }
    }
  • FailbackRegistry(URL url)
public FailbackRegistry(URL url) {
        super(url);
        int retryPeriod = url.getParameter(Constants.REGISTRY_RETRY_PERIOD_KEY, Constants.DEFAULT_REGISTRY_RETRY_PERIOD);
        this.retryFuture = retryExecutor.scheduleWithFixedDelay(new Runnable() {
            public void run() {
                // 檢測並連接註冊中心
                try {
                    retry();
                } catch (Throwable t) { // 防禦性容錯
                    logger.error("Unexpected error occur at failed retry, cause: " + t.getMessage(), t);
                }
            }
        }, retryPeriod, retryPeriod, TimeUnit.MILLISECONDS);
    }

//retry方法
 protected void retry() {
        if (!failedRegistered.isEmpty()) {
            Set<URL> failed = new HashSet<URL>(failedRegistered);
            if (failed.size() > 0) {
                if (logger.isInfoEnabled()) {
                    logger.info("Retry register " + failed);
                }
                try {
                    for (URL url : failed) {
                        try {
                            doRegister(url);
                            failedRegistered.remove(url);
                        } catch (Throwable t) { // 忽略所有異常,等待下次重試
                            logger.warn("Failed to retry register " + failed + ", waiting for again, cause: " + t.getMessage(), t);
                        }
                    }
                } catch (Throwable t) { // 忽略所有異常,等待下次重試
                    logger.warn("Failed to retry register " + failed + ", waiting for again, cause: " + t.getMessage(), t);
                }
            }
        }
        if (!failedUnregistered.isEmpty()) {
            Set<URL> failed = new HashSet<URL>(failedUnregistered);
            if (failed.size() > 0) {
                if (logger.isInfoEnabled()) {
                    logger.info("Retry unregister " + failed);
                }
                try {
                    for (URL url : failed) {
                        try {
                            doUnregister(url);
                            failedUnregistered.remove(url);
                        } catch (Throwable t) { // 忽略所有異常,等待下次重試
                            logger.warn("Failed to retry unregister  " + failed + ", waiting for again, cause: " + t.getMessage(), t);
                        }
                    }
                } catch (Throwable t) { // 忽略所有異常,等待下次重試
                    logger.warn("Failed to retry unregister  " + failed + ", waiting for again, cause: " + t.getMessage(), t);
                }
            }
        }
        if (!failedSubscribed.isEmpty()) {
            Map<URL, Set<NotifyListener>> failed = new HashMap<URL, Set<NotifyListener>>(failedSubscribed);
            for (Map.Entry<URL, Set<NotifyListener>> entry : new HashMap<URL, Set<NotifyListener>>(failed).entrySet()) {
                if (entry.getValue() == null || entry.getValue().size() == 0) {
                    failed.remove(entry.getKey());
                }
            }
            if (failed.size() > 0) {
                if (logger.isInfoEnabled()) {
                    logger.info("Retry subscribe " + failed);
                }
                try {
                    for (Map.Entry<URL, Set<NotifyListener>> entry : failed.entrySet()) {
                        URL url = entry.getKey();
                        Set<NotifyListener> listeners = entry.getValue();
                        for (NotifyListener listener : listeners) {
                            try {
                                doSubscribe(url, listener);
                                listeners.remove(listener);
                            } catch (Throwable t) { // 忽略所有異常,等待下次重試
                                logger.warn("Failed to retry subscribe " + failed + ", waiting for again, cause: " + t.getMessage(), t);
                            }
                        }
                    }
                } catch (Throwable t) { // 忽略所有異常,等待下次重試
                    logger.warn("Failed to retry subscribe " + failed + ", waiting for again, cause: " + t.getMessage(), t);
                }
            }
        }
        if (!failedUnsubscribed.isEmpty()) {
            Map<URL, Set<NotifyListener>> failed = new HashMap<URL, Set<NotifyListener>>(failedUnsubscribed);
            for (Map.Entry<URL, Set<NotifyListener>> entry : new HashMap<URL, Set<NotifyListener>>(failed).entrySet()) {
                if (entry.getValue() == null || entry.getValue().size() == 0) {
                    failed.remove(entry.getKey());
                }
            }
            if (failed.size() > 0) {
                if (logger.isInfoEnabled()) {
                    logger.info("Retry unsubscribe " + failed);
                }
                try {
                    for (Map.Entry<URL, Set<NotifyListener>> entry : failed.entrySet()) {
                        URL url = entry.getKey();
                        Set<NotifyListener> listeners = entry.getValue();
                        for (NotifyListener listener : listeners) {
                            try {
                                doUnsubscribe(url, listener);
                                listeners.remove(listener);
                            } catch (Throwable t) { // 忽略所有異常,等待下次重試
                                logger.warn("Failed to retry unsubscribe " + failed + ", waiting for again, cause: " + t.getMessage(), t);
                            }
                        }
                    }
                } catch (Throwable t) { // 忽略所有異常,等待下次重試
                    logger.warn("Failed to retry unsubscribe " + failed + ", waiting for again, cause: " + t.getMessage(), t);
                }
            }
        }
        if (!failedNotified.isEmpty()) {
            Map<URL, Map<NotifyListener, List<URL>>> failed = new HashMap<URL, Map<NotifyListener, List<URL>>>(failedNotified);
            for (Map.Entry<URL, Map<NotifyListener, List<URL>>> entry : new HashMap<URL, Map<NotifyListener, List<URL>>>(failed).entrySet()) {
                if (entry.getValue() == null || entry.getValue().size() == 0) {
                    failed.remove(entry.getKey());
                }
            }
            if (failed.size() > 0) {
                if (logger.isInfoEnabled()) {
                    logger.info("Retry notify " + failed);
                }
                try {
                    for (Map<NotifyListener, List<URL>> values : failed.values()) {
                        for (Map.Entry<NotifyListener, List<URL>> entry : values.entrySet()) {
                            try {
                                NotifyListener listener = entry.getKey();
                                List<URL> urls = entry.getValue();
                                listener.notify(urls);
                                values.remove(listener);
                            } catch (Throwable t) { // 忽略所有異常,等待下次重試
                                logger.warn("Failed to retry notify " + failed + ", waiting for again, cause: " + t.getMessage(), t);
                            }
                        }
                    }
                } catch (Throwable t) { // 忽略所有異常,等待下次重試
                    logger.warn("Failed to retry notify " + failed + ", waiting for again, cause: " + t.getMessage(), t);
                }
            }
        }
    }
  • 模板方法
    protected abstract void doRegister(URL url);

    protected abstract void doUnregister(URL url);

    protected abstract void doSubscribe(URL url, NotifyListener listener);

    protected abstract void doUnsubscribe(URL url, NotifyListener listener);

ZookeeperRegistry

-實現了doRegister,doUnregister,doSubscribe,doUnsubscribe
- public ZookeeperRegistry(URL url, ZookeeperTransporter zookeeperTransporter)

    public ZookeeperRegistry(URL url, ZookeeperTransporter zookeeperTransporter) {
        super(url);
        if (url.isAnyHost()) {
            throw new IllegalStateException("registry address == null");
        }
        String group = url.getParameter(Constants.GROUP_KEY, DEFAULT_ROOT);
        if (!group.startsWith(Constants.PATH_SEPARATOR)) {
            group = Constants.PATH_SEPARATOR + group;
        }
        this.root = group;
        zkClient = zookeeperTransporter.connect(url);
        zkClient.addStateListener(new StateListener() {
            public void stateChanged(int state) {
                if (state == RECONNECTED) {
                    try {
                        recover();
                    } catch (Exception e) {
                        logger.error(e.getMessage(), e);
                    }
                }
            }
        });
    }
發佈了54 篇原創文章 · 獲贊 56 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章