Dubbo客戶端初始化過程

導讀

  • 上一篇我們分析了 Dubbo設計之ExtensionLoader,瞭解了其原理之後,就方便我們分析Dubbo客戶端得初始化過程了。
  • 關鍵字Dubbo客戶端初始化、Netty入門級用法
  • Dubbo版本 : 2.7+
  • 入口: ReferenceConfig

客戶端初始化:

  • 下面是Dubbo消費者API使用方式的示例代碼
// 創建ReferenceConfig,此類會有一系列的初始化準備過程
ReferenceConfig<AlipayInfoAbility> reference = new ReferenceConfig<>();
// 設置消費者應用配置
reference.setApplication(new ApplicationConfig("dubbo-demo-api-consumer"));
// 設置註冊地址
reference.setRegistry(new RegistryConfig("zookeeper://" + zookeeperHost + ":" + zookeeperPort));
// 設置引用的API接口
reference.setInterface(AlipayInfoAbility.class);
// 關閉服務啓動檢查(建議不要關閉,找不到服務要在應用發佈時就要解決)
reference.setCheck(false);
// 獲取引用
AlipayInfoAbility service = reference.get();

上述代碼中, reference.get()是整個調用方獲取接口代理得核心起始點。主要核心邏輯如下:
設置接口級別得公共參數 Map<String, String> map:爲初始化代理引用ref做準備,例如:設置key = side,value = consumer,表示當前是服務調用方;通過 MetricsConfigRegistryConfig(註冊中心)ConfigCenterConfig(配置中心)ApplicationConfigModuleConfigConsumerConfig(可以指定消費端線程池類型:cached, fixed, limit, eager;核心線程數、最大線程數、隊列大小、共享連接數shareconnections(TCP長連接得客戶端數量))MethodConfig(可以指定方法相關得參數配置:可以設置重試次數、併發數(executes)、是否啓動session粘連(sticky)、方法是否有異步回調等)等初始化配置來填充 map。
② 接着就是調用創建代理引用得核心方法org.apache.dubbo.config.ReferenceConfig#createProxy

       private T createProxy(Map<String, String> map) {

        if (shouldJvmRefer(map)) { // 1.判斷是否是本地調用(通過scope=local、injvm=false都可以),重要用於本地測試,防止調用遠程服務器
            URL url = new URL(LOCAL_PROTOCOL, LOCALHOST_VALUE, 0, interfaceClass.getName()).addParameters(map);
            invoker = REF_PROTOCOL.refer(interfaceClass, url); // 通過自適應擴展點機制獲取代理Invoker
            if (logger.isInfoEnabled()) {
                logger.info("Using injvm service " + interfaceClass.getName());
            }
        } else { // 2.不是本地調用
            urls.clear(); // reference retry init will add url to urls, lead to OOM
            if (url != null && url.length() > 0) { // user specified URL, could be peer-to-peer address, or register center's address.
                String[] us = SEMICOLON_SPLIT_PATTERN.split(url);
                if (us != null && us.length > 0) {
                    for (String u : us) {
                        URL url = URL.valueOf(u);
                        if (StringUtils.isEmpty(url.getPath())) {
                            url = url.setPath(interfaceName);
                        }
                        if (REGISTRY_PROTOCOL.equals(url.getProtocol())) { // 3.協議類型是註冊(registry)
                            urls.add(url.addParameterAndEncoded(REFER_KEY, StringUtils.toQueryString(map)));
                        } else {
                            urls.add(ClusterUtils.mergeUrl(url, map));
                        }
                    }
                }
            } else { // assemble URL from register center's configuration
                // if protocols not injvm checkRegistry
                if (!LOCAL_PROTOCOL.equalsIgnoreCase(getProtocol())){ // 若註冊協議不是injvm
                    // 校驗註冊中心配置(RegistryConfig),會向後兼容-Ddubbo.registry.address啓動參數
                    checkRegistry();
                    // 4.通過配置中心相關設置解析成URL(一般註冊中心都使用 zookeeper,沒配置默認是dubbo)
                    // 執行完之後會將protocol屬性key設置爲registry,當下面調用協議自適應擴展點得時候就會找到 Registryprotocol
                    List<URL> us = loadRegistries(false);
                    if (CollectionUtils.isNotEmpty(us)) {
                        for (URL u : us) {
                            // 5.加載監控配置,顯式設置直接使用。沒顯式設置:判斷是否有logstat監控擴展點工廠,沒有會使用Dubbo得監控
                            URL monitorUrl = loadMonitor(u);
                            if (monitorUrl != null) {
                                map.put(MONITOR_KEY, URL.encode(monitorUrl.toFullString()));
                            }
                            urls.add(u.addParameterAndEncoded(REFER_KEY, StringUtils.toQueryString(map)));
                        }
                    }
                    if (urls.isEmpty()) {
                        throw new IllegalStateException("No such any registry to reference " + interfaceName + " on the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", please config <dubbo:registry address=\"...\" /> to your spring config.");
                    }
                }
            }

            if (urls.size() == 1) {// 註冊中心單節點
                // 6.通過Registryprotocol$Apaptive 自適應擴展點實現獲取Invoker
                invoker = REF_PROTOCOL.refer(interfaceClass, urls.get(0));
            } else { // 註冊中心集羣
                List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
                URL registryURL = null;
                for (URL url : urls) {
                    // 7.通過Registryprotocol$Apaptive獲取Invoker,放入到列表中
                    invokers.add(REF_PROTOCOL.refer(interfaceClass, url));
                    if (REGISTRY_PROTOCOL.equals(url.getProtocol())) {
                        registryURL = url; // use last registry url
                    }
                }
                if (registryURL != null) { // registry url is available
                    // use RegistryAwareCluster only when register's CLUSTER is available
                    URL u = registryURL.addParameter(CLUSTER_KEY, RegistryAwareCluster.NAME);
                    // The invoker wrap relation would be: RegistryAwareClusterInvoker(StaticDirectory) -> FailoverClusterInvoker(RegistryDirectory, will execute route) -> Invoker
                    invoker = CLUSTER.join(new StaticDirectory(u, invokers));// 8.通過集羣RegistryAwareCluster對Invoker進行包裝
                } else { // not a registry url, must be direct invoke.
                    invoker = CLUSTER.join(new StaticDirectory(invokers));
                }
            }
        }
        // 若開啓Dubbo服務檢查,並且服務接口不可用,則直接拋異常
        if (shouldCheck() && !invoker.isAvailable()) {
            throw new IllegalStateException("Failed to check the status of the service " + interfaceName + ". No provider available for the service " + (group == null ? "" : group + "/") + interfaceName + (version == null ? "" : ":" + version) + " from the url " + invoker.getUrl() + " to the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion());
        }
        if (logger.isInfoEnabled()) {
            logger.info("Refer dubbo service " + interfaceClass.getName() + " from url " + invoker.getUrl());
        }
        /**
         * @since 2.7.0
         * ServiceData Store
         */
        MetadataReportService metadataReportService = null;
        if ((metadataReportService = getMetadataReportService()) != null) {
            URL consumerURL = new URL(CONSUMER_PROTOCOL, map.remove(REGISTER_IP_KEY), 0, map.get(INTERFACE_KEY), map);
            metadataReportService.publishConsumer(consumerURL);// 9.消費者元數據信息發佈
        }
        // create service proxy
        return (T) PROXY_FACTORY.getProxy(invoker);// 10.真正開始創建代理對象,通過代理工廠創建代理引用(代理工廠的默認擴展點名是 javassist )
    }

上述方法中有幾處重要得地方:1、2、3、4、5、6(單節點)、7、8(集羣)、9、10;其中6(7)、8、10是重中之重。下面主要來分析6(7)、8、10這幾個關鍵步驟
6(7) 步驟核心子流程如下:
① 調用協議的自適應擴展點獲取真正端得協議擴展點(Registryprotocol),(擴展點的原理:Dubbo設計之ExtensionLoader),但是由於Protocol擴展點有包裝類(ProtocolFilterWrapper、ProtocolListenerWrapper),所以獲取真正的擴展實現之後會被包裝類包裝起來: ProtocolFilterWrapper.refer -> ProtocolListenerWrapper.refer -> Registryprotocol.refer

// ProtocolFilterWrapper    
    @Override
    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
        if (REGISTRY_PROTOCOL.equals(url.getProtocol())) {
            return protocol.refer(type, url); // 由於當前協議是registry,調用ProtocolListenerWrapper的 refer方法
        }
        return buildInvokerChain(protocol.refer(type, url), REFERENCE_FILTER_KEY, CommonConstants.CONSUMER);
    }

// ProtocolListenerWrapper    
    @Override
    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
        if (REGISTRY_PROTOCOL.equals(url.getProtocol())) {
            return protocol.refer(type, url); // 由於當前協議是registry,調用 RegistryProtocol 的refer方法
        }
        return new ListenerInvokerWrapper<T>(protocol.refer(type, url),
                Collections.unmodifiableList(
                        ExtensionLoader.getExtensionLoader(InvokerListener.class)
                                .getActivateExtension(url, INVOKER_LISTENER_KEY)));
    }

② 接着通過ProtocolListenerWrapper調用真實的擴展實現RegistryProtocol 的refer方法:

// RegistryProtocol 
    @Override
    @SuppressWarnings("unchecked")
    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
        // 重新構建一個protocol爲 `zookeeper`的url,如果url沒有指定protocol的話,默認是 `dubbo`
        url = URLBuilder.from(url)
                .setProtocol(url.getParameter(REGISTRY_KEY, DEFAULT_REGISTRY))
                .removeParameter(REGISTRY_KEY)
                .build();
        // 調用 `RegistryFactory擴展點`的自適應擴展實現,此處根據url中的protocol=zookeeper,會去找到真正的擴展點實現`ZookeeperRegistryFactory`,然後調用其getRegistry方法(實際上此方法繼承自父類 AbstractRegistryFactory)生成ZookeeperRegistry實例。
        Registry registry = registryFactory.getRegistry(url);
        if (RegistryService.class.equals(type)) {
            return proxyFactory.getInvoker((T) registry, type, url);
        }

        // group="a,b" or group="*"
        Map<String, String> qs = StringUtils.parseQueryString(url.getParameterAndDecoded(REFER_KEY));
        String group = qs.get(GROUP_KEY);// 假設group爲空的情況
        if (group != null && group.length() > 0) {
            if ((COMMA_SPLIT_PATTERN.split(group)).length > 1 || "*".equals(group)) {
                return doRefer(getMergeableCluster(), registry, type, url);// 不執行此處代碼
            }
        }
        return doRefer(cluster, registry, type, url);// 通過集羣擴展點對註冊實例進行包裝
    }

③ 調用 ZookeeperRegistryFactory 的 getRegistry方法(繼承自 AbstractRegistryFactory )獲取註冊實現:

// AbstractRegistryFactory 
    @Override
    public Registry getRegistry(URL url) {
        // 構建一個路徑爲RegistryService的註冊URL
        url = URLBuilder.from(url)
                .setPath(RegistryService.class.getName())
                .addParameter(INTERFACE_KEY, RegistryService.class.getName())
                .removeParameters(EXPORT_KEY, REFER_KEY)
                .build();
        String key = url.toServiceStringWithoutResolving();
        // Lock the registry access process to ensure a single instance of the registry
        LOCK.lock();
        try {
            Registry registry = REGISTRIES.get(key);// 從緩存中取,不等於空直接返回
            if (registry != null) {
                return registry;
            }
            //create registry by spi/ioc
            registry = createRegistry(url); // 模板方法,通過具體的實現類(此處是ZookeeperRegistryFactory)去獲取註冊實例
            if (registry == null) {
                throw new IllegalStateException("Can not create registry " + url);
            }
            REGISTRIES.put(key, registry); // 獲取之後放入緩存中,防止併發場景下重複創建
            return registry;
        } finally {
            // Release the lock
            LOCK.unlock();
        }
    }

    protected abstract Registry createRegistry(URL url); // 抽象模板方法

// ZookeeperRegistryFactory 的方法實現
    @Override
    public Registry createRegistry(URL url) {
        // 創建註冊實例,註冊的url爲 zookeeper://xxx:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-api-consumer&dubbo=XXX&interface=org.apache.dubbo.registry.RegistryService&pid=13740&timestamp=1640015273548
        return new ZookeeperRegistry(url, zookeeperTransporter);
    }
// 構造器
    public ZookeeperRegistry(URL url, ZookeeperTransporter zookeeperTransporter) {
        super(url);
        if (url.isAnyHost()) {
            throw new IllegalStateException("registry address == null");
        }
        String group = url.getParameter(GROUP_KEY, DEFAULT_ROOT);
        if (!group.startsWith(PATH_SEPARATOR)) {
            group = PATH_SEPARATOR + group;
        }
        this.root = group;
        zkClient = zookeeperTransporter.connect(url);// 連接zk server端
        zkClient.addStateListener(state -> {
            if (state == StateListener.RECONNECTED) {
                try {
                    recover(); // 故障恢復
                } catch (Exception e) {
                    logger.error(e.getMessage(), e);
                }
            }
        });
    }
// FailbackRegistry( ZookeeperRegistry的父類 )
    @Override
    protected void recover() throws Exception {
        // register
        Set<URL> recoverRegistered = new HashSet<URL>(getRegistered());// 獲取已經註冊的URL
        if (!recoverRegistered.isEmpty()) {
            if (logger.isInfoEnabled()) {
                logger.info("Recover register url " + recoverRegistered);
            }
            for (URL url : recoverRegistered) {
                addFailedRegistered(url);// 添加到失敗的列表中,並通過時間輪(HashedWheelTimer)定時重試
            }
        }
        // subscribe
        Map<URL, Set<NotifyListener>> recoverSubscribed = new HashMap<URL, Set<NotifyListener>>(getSubscribed()); // 獲取訂閱的URL跟對應的通知狀態變更的監聽器
        if (!recoverSubscribed.isEmpty()) {
            if (logger.isInfoEnabled()) {
                logger.info("Recover subscribe url " + recoverSubscribed.keySet());
            }
            for (Map.Entry<URL, Set<NotifyListener>> entry : recoverSubscribed.entrySet()) {
                URL url = entry.getKey();
                for (NotifyListener listener : entry.getValue()) {
                    addFailedSubscribed(url, listener);// 添加到失敗訂閱列表中,並通過時間輪(HashedWheelTimer)定時重試
                }
            }
        }
    }

④ 調用 org.apache.dubbo.registry.integration.RegistryProtocol#doRefer方法,通過集羣擴展點Cluster對本地註冊目錄進行合併:


private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
        // 創建本地註冊目錄(RegistryDirectory本身就是一個org.apache.dubbo.registry.NotifyListener),當監聽到服務端地址變化時,更新本地url信息
        RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
        directory.setRegistry(registry);
        directory.setProtocol(protocol);
        // all attributes of REFER_KEY
        Map<String, String> parameters = new HashMap<String, String>(directory.getUrl().getParameters());
        URL subscribeUrl = new URL(CONSUMER_PROTOCOL, parameters.remove(REGISTER_IP_KEY), 0, type.getName(), parameters);
        if (!ANY_VALUE.equals(url.getServiceInterface()) && url.getParameter(REGISTER_KEY, true)) {
            // 設置消費的URL
            directory.setRegisteredConsumerUrl(getRegisteredConsumerUrl(subscribeUrl, url));
            // 調用註冊實例的註冊方法,將消費的URL註冊到本地
            registry.register(directory.getRegisteredConsumerUrl());
        }
        // 構建路由鏈 (根據優先級依次爲 MockRouterFactory、TagRouterFactor、AppRouterFactory、ServiceRouterFactory)
        directory.buildRouterChain(subscribeUrl);

        // 訂閱URL(consumer://127.0..1/com.alibaba.mp.alipay.AlipayInfoAbility?application=dubbo-demo-api-consumer&category=providers,configurators,routers&dubbo=2.0.2&interface=com.alibaba.mp.alipay.AlipayInfoAbility&lazy=false&methods=getAlipayInfo&pid=11856&side=consumer&sticky=false&timestamp=1640020348831)
        directory.subscribe(subscribeUrl.addParameter(CATEGORY_KEY,
                PROVIDERS_CATEGORY + "," + CONFIGURATORS_CATEGORY + "," + ROUTERS_CATEGORY));

        // 合併成一個virtual的Invoker
        Invoker invoker = cluster.join(directory);
        ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory);
        return invoker;
    }

⑤ 註冊消費的URL、構建調用路由鏈、訂閱要消費的URL(RegistryDirectory中)

// FailbackRegistry 重寫了AbstractRegistry的register方法,增加將消費者URL註冊到服務端的的邏輯
    @Override
    public void register(URL url) {
        // 緩存到本地
        super.register(url);
        removeFailedRegistered(url);
        removeFailedUnregistered(url);
        try {
            // Sending a registration request to the server side
            doRegister(url); // 模板方法,向服務端發送註冊請求
        } catch (Exception e) {
            Throwable t = e;
            ...
        }
    }
// ZookeeperRegistry
    @Override
    public void doRegister(URL url) {
        try {
            // 在zk上創建提供方URL的臨時節點(提供方目錄是臨時的)
            zkClient.create(toUrlPath(url), url.getParameter(DYNAMIC_KEY, true));
        } catch (Throwable e) {
            ...
        }
    }

// 構建路由鏈,用於後續從集羣中選擇路由時使用
    public void buildRouterChain(URL url) {
        this.setRouterChain(RouterChain.buildChain(url));
    }

// RouterChain
    public static <T> RouterChain<T> buildChain(URL url) {
        return new RouterChain<>(url);
    }

    private RouterChain(URL url) {

        // 自然序獲取RouterFactory所有的激活擴展點實現,(MockRouterFactory、TagRouterFactor、AppRouterFactory、ServiceRouterFactory)
        List<RouterFactory> extensionFactories = ExtensionLoader.getExtensionLoader(RouterFactory.class)
                .getActivateExtension(url, (String[]) null);
        // 獲取路由集合
        List<Router> routers = extensionFactories.stream()
                .map(factory -> factory.getRouter(url))
                .collect(Collectors.toList());

        initWithRouters(routers);
    }
// RegistryDirectory 的訂閱方法
    public void subscribe(URL url) {
        // 緩存到本地註冊目錄中
        setConsumerUrl(url);
        // 添加 節點變更監聽器用於通知服務上下線
        CONSUMER_CONFIGURATION_LISTENER.addNotifyListener(this);
        // 通知 configurator/router 變更
        serviceConfigurationListener = new ReferenceConfigurationListener(this, url);
        // 開始訂閱消費URL,每個對應一個註冊目錄,並註冊與遠端服務子節點之間的映射關係,當zk節點發生變化時會回調當前監聽器更新本地消費URL
        registry.subscribe(url, this);
    }

// FailbackRegistry,listener爲 RegistryDirectory
    @Override
    public void subscribe(URL url, NotifyListener listener) {
        super.subscribe(url, listener);
        removeFailedSubscribed(url, listener);
        try {
            // Sending a subscription request to the server side
            doSubscribe(url, listener); // 模板方法,調用子類(ZookeeperRegistry)的訂閱方法
        } catch (Exception e) {
            Throwable t = e;
            ...
        }
    }
// ZookeeperRegistry 的模板實現方法
    @Override
    public void doSubscribe(final URL url, final NotifyListener listener) {
        try {
            if (ANY_VALUE.equals(url.getServiceInterface())) {
                ...
            } else {
                List<URL> urls = new ArrayList<>();
                // toCategoriesPath:
                // 0 = "/dubbo/com.alibaba.mp.alipay.AlipayInfoAbility/providers"
                // 1 = "/dubbo/com.alibaba.mp.alipay.AlipayInfoAbility/configurators"
                // 2 = "/dubbo/com.alibaba.mp.alipay.AlipayInfoAbility/routers"
                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) {
                        zkListener = listeners.get(listener);
                        // 創建zk子節點監聽器,用於監聽節點變化,然後調用notify()方法變更本地目錄
                        listeners.putIfAbsent(listener, (parentPath, currentChilds) -> ZookeeperRegistry.this.notify(url, listener, toUrlsWithEmpty(url, parentPath, currentChilds)));
                    }
                    // 創建持久化節點
                    zkClient.create(path, false);
                    List<String> children = zkClient.addChildListener(path, zkListener);
                    if (children != null) {
                        urls.addAll(toUrlsWithEmpty(url, path, children));
                    }
                }
                // 通知RegistryDirectory監聽器,修改本地URL
                notify(url, listener, urls);
            }
        } catch (Throwable e) {
            ...
        }
    }
// FailbackRegistry
    @Override
    protected void notify(URL url, NotifyListener listener, List<URL> urls) {
        if (url == null) {
            throw new IllegalArgumentException("notify url == null");
        }
        if (listener == null) {
            throw new IllegalArgumentException("notify listener == null");
        }
        try {
            doNotify(url, listener, urls); // 模板方法,ZookeeperRegistry 實現通知變更
        } catch (Exception t) {
            ...
        }
    }
// AbstractRegistry
    protected void notify(URL url, NotifyListener listener, List<URL> urls) {
        if (url == null) {
            throw new IllegalArgumentException("notify url == null");
        }
        if (listener == null) {
            throw new IllegalArgumentException("notify listener == null");
        }
        if ((CollectionUtils.isEmpty(urls))
                && !ANY_VALUE.equals(url.getServiceInterface())) {
            logger.warn("Ignore empty notify urls for subscribe url " + url);
            return;
        }
        if (logger.isInfoEnabled()) {
            logger.info("Notify urls for subscribe url " + url + ", urls: " + urls);
        }
        // keep every provider's category.
        Map<String, List<URL>> result = new HashMap<>();
        for (URL u : urls) {
            if (UrlUtils.isMatch(url, u)) {
                String category = u.getParameter(CATEGORY_KEY, DEFAULT_CATEGORY);
                List<URL> categoryList = result.computeIfAbsent(category, k -> new ArrayList<>());
                categoryList.add(u);
            }
        }
        if (result.size() == 0) {
            return;
        }
        // 將提供方的URL放入到已通知的對象映射中
        Map<String, List<URL>> categoryNotified = notified.computeIfAbsent(url, u -> new ConcurrentHashMap<>());
        for (Map.Entry<String, List<URL>> entry : result.entrySet()) {
            String category = entry.getKey();
            List<URL> categoryList = entry.getValue();
            categoryNotified.put(category, categoryList);
            // 調用監聽器通知變更,listener = RegistryDirectory
            listener.notify(categoryList);
            // We will update our cache file after each notification.
            // When our Registry has a subscribe failure due to network jitter, we can return at least the existing cache URL.
            saveProperties(url);
        }
    }
// RegistryDirectory,更新本地對應的目錄
    @Override
    public synchronized void notify(List<URL> urls) {
        // 根據 configurators、routers、providers進行分組
        Map<String, List<URL>> categoryUrls = urls.stream()
                .filter(Objects::nonNull)
                .filter(this::isValidCategory)
                .filter(this::isNotCompatibleFor26x)
                .collect(Collectors.groupingBy(url -> {
                    if (UrlUtils.isConfigurator(url)) {
                        return CONFIGURATORS_CATEGORY;
                    } else if (UrlUtils.isRoute(url)) {
                        return ROUTERS_CATEGORY;
                    } else if (UrlUtils.isProvider(url)) {
                        return PROVIDERS_CATEGORY;
                    }
                    return "";
                }));
        // 若監聽到category有變更,則取代之前的值
        List<URL> configuratorURLs = categoryUrls.getOrDefault(CONFIGURATORS_CATEGORY, Collections.emptyList());
        this.configurators = Configurator.toConfigurators(configuratorURLs).orElse(this.configurators);
        // 若監聽到有新得路由,則添加到路由鏈中
        List<URL> routerURLs = categoryUrls.getOrDefault(ROUTERS_CATEGORY, Collections.emptyList());
        toRouters(routerURLs).ifPresent(this::addRouters);

        // providers
        List<URL> providerURLs = categoryUrls.getOrDefault(PROVIDERS_CATEGORY, Collections.emptyList());
        // 刷新+覆蓋
        refreshOverrideAndInvoker(providerURLs);
    }

    private void refreshOverrideAndInvoker(List<URL> urls) {
        // mock zookeeper://xxx?mock=return null
        overrideDirectoryUrl();
        // 刷新本地緩存的Invokers
        refreshInvoker(urls);
    }

private void refreshInvoker(List<URL> invokerUrls) {
        Assert.notNull(invokerUrls, "invokerUrls should not be null");

        if (invokerUrls.size() == 1
                && invokerUrls.get(0) != null
                && EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) {
            this.forbidden = true; // Forbid to access
            this.invokers = Collections.emptyList();
            routerChain.setInvokers(this.invokers);
            destroyAllInvokers(); // Close all invokers
        } else {
            this.forbidden = false; // Allow to access
            Map<String, Invoker<T>> oldUrlInvokerMap = this.urlInvokerMap; // local reference
            if (invokerUrls == Collections.<URL>emptyList()) {
                invokerUrls = new ArrayList<>();
            }
            if (invokerUrls.isEmpty() && this.cachedInvokerUrls != null) {
                invokerUrls.addAll(this.cachedInvokerUrls);
            } else {
                this.cachedInvokerUrls = new HashSet<>();
                this.cachedInvokerUrls.addAll(invokerUrls);//Cached invoker urls, convenient for comparison
            }
            if (invokerUrls.isEmpty()) {
                return;
            }
            Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls);// Translate url list to Invoker map

            /**
             * If the calculation is wrong, it is not processed.
             *
             * 1. The protocol configured by the client is inconsistent with the protocol of the server.
             *    eg: consumer protocol = dubbo, provider only has other protocol services(rest).
             * 2. The registration center is not robust and pushes illegal specification data.
             *
             */
            if (CollectionUtils.isEmptyMap(newUrlInvokerMap)) {
                logger.error(new IllegalStateException("urls to invokers error .invokerUrls.size :" + invokerUrls.size() + ", invoker.size :0. urls :" + invokerUrls
                        .toString()));
                return;
            }

            List<Invoker<T>> newInvokers = Collections.unmodifiableList(new ArrayList<>(newUrlInvokerMap.values()));
            // pre-route and build cache, notice that route cache should build on original Invoker list.
            // toMergeMethodInvokerMap() will wrap some invokers having different groups, those wrapped invokers not should be routed.
            routerChain.setInvokers(newInvokers);
            // 覆蓋本地緩存,調用的時候就是從此成員變量中選取的
            this.invokers = multiGroup ? toMergeInvokerList(newInvokers) : newInvokers;
            this.urlInvokerMap = newUrlInvokerMap;

            try {
                destroyUnusedInvokers(oldUrlInvokerMap, newUrlInvokerMap); // Close the unused Invoker
            } catch (Exception e) {
                logger.warn("destroyUnusedInvokers error. ", e);
            }
        }
    }

    private Map<String, Invoker<T>> toInvokers(List<URL> urls) {
        Map<String, Invoker<T>> newUrlInvokerMap = new HashMap<>();
        if (urls == null || urls.isEmpty()) {
            return newUrlInvokerMap;
        }
        Set<String> keys = new HashSet<>();
        String queryProtocols = this.queryMap.get(PROTOCOL_KEY);
        for (URL providerUrl : urls) {
            // If protocol is configured at the reference side, only the matching protocol is selected
            if (queryProtocols != null && queryProtocols.length() > 0) {
                boolean accept = false;
                String[] acceptProtocols = queryProtocols.split(",");
                for (String acceptProtocol : acceptProtocols) {
                    if (providerUrl.getProtocol().equals(acceptProtocol)) {
                        accept = true;
                        break;
                    }
                }
                if (!accept) {
                    continue;
                }
            }
            if (EMPTY_PROTOCOL.equals(providerUrl.getProtocol())) {
                continue;
            }
            // protocol = dubbo
            if (!ExtensionLoader.getExtensionLoader(Protocol.class).hasExtension(providerUrl.getProtocol())) {
                logger.error(new IllegalStateException("Unsupported protocol " + providerUrl.getProtocol() +
                        " in notified url: " + providerUrl + " from registry " + getUrl().getAddress() +
                        " to consumer " + NetUtils.getLocalHost() + ", supported protocol: " +
                        ExtensionLoader.getExtensionLoader(Protocol.class).getSupportedExtensions()));
                continue;
            }
            
            URL url = mergeUrl(providerUrl);

            String key = url.toFullString(); // The parameter urls are sorted
            if (keys.contains(key)) { // Repeated url
                continue;
            }
            keys.add(key);
            // Cache key is url that does not merge with consumer side parameters, regardless of how the consumer combines parameters, if the server url changes, then refer again
            Map<String, Invoker<T>> localUrlInvokerMap = this.urlInvokerMap; // local reference
            Invoker<T> invoker = localUrlInvokerMap == null ? null : localUrlInvokerMap.get(key);
            if (invoker == null) { // Not in the cache, refer again
                try {
                    boolean enabled = true;
                    if (url.hasParameter(DISABLED_KEY)) {
                        enabled = !url.getParameter(DISABLED_KEY, false);
                    } else {
                        enabled = url.getParameter(ENABLED_KEY, true);
                    }
                    // 默認情況下,enabled = true
                    if (enabled) {
                        // 根據當前key創建一個委派Invoker:InvokerDelegate。
                        // 重點:而此處url的protocol = dubbo,那麼 protocol.refer(serviceType, url)會執行 Dubboprotocol的refer方法
                        // 由於此處的 protocol引用是自適應擴展點,所以還是會經過ProtocolFilterWrapper跟ProtocolListenerWrapper進行包裝:ProtocolFilterWrapper.refer -> ProtocolListenerWrapper.refer -> Dubboprotocol.refer
                        invoker = new InvokerDelegate<>(protocol.refer(serviceType, url), url, providerUrl);
                    }
                } catch (Throwable t) {
                    logger.error("Failed to refer invoker for interface:" + serviceType + ",url:(" + url + ")" + t.getMessage(), t);
                }
                if (invoker != null) { // Put new invoker in cache
                    newUrlInvokerMap.put(key, invoker);
                }
            } else {
                newUrlInvokerMap.put(key, invoker);
            }
        }
        keys.clear();
        return newUrlInvokerMap;
    }
// ProtocolFilterWrapper 的refer方法
    @Override
    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
        if (REGISTRY_PROTOCOL.equals(url.getProtocol())) {// 協議是dubbo,此處不成立
            return protocol.refer(type, url);
        }
        // 調用 ProtocolListenerWrapper 的refer方法先生成一個Invoker
        // 將生成的Invoker與Filter擴展點構建消費端的Invoker鏈,REFERENCE_FILTER_KEY = reference.filter,
        // 經過返回 ProtocolFilterWrapper 之後,生成的是 CallbackRegistrationInvoker
        return buildInvokerChain(protocol.refer(type, url), REFERENCE_FILTER_KEY, CommonConstants.CONSUMER);
    }
// 構建Filter鏈
    private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
        // 此處的 invoker是ProtocolListenerWrapper.refer執行之後的Invoker,即 ListenerInvokerWrapper
        Invoker<T> last = invoker;
        // 獲取消費者端的所有滿足條件的激活擴展點,此處會根據排序字段升序排列
        // ConsumerContextFilter -> FutureFilter -> MonitorFilter
        List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);

        if (!filters.isEmpty()) {
            // 循環從後往前組成Invoker鏈,爲了調用時按照設定的順序執行
            // 循環順序:MonitorFilter -> FutureFilter -> ConsumerContextFilter
            // 請求處理順序:ConsumerContextFilter -> FutureFilter -> MonitorFilter
            for (int i = filters.size() - 1; i >= 0; i--) {
                final Filter filter = filters.get(i);
                final Invoker<T> next = last;
                last = new Invoker<T>() {
                    ...
                    ...
                    @Override
                    public Result invoke(Invocation invocation) throws RpcException {
                        Result asyncResult;
                        try {
                            asyncResult = filter.invoke(next, invocation);
                        } catch (Exception e) {
                            // onError callback
                            if (filter instanceof ListenableFilter) {
                                Filter.Listener listener = ((ListenableFilter) filter).listener();
                                if (listener != null) {
                                    // 鉤子方法
                                    listener.onError(e, invoker, invocation);
                                }
                            }
                            throw e;
                        }
                        return asyncResult;
                    }

                    @Override
                    public void destroy() {
                        invoker.destroy();
                    }

                    @Override
                    public String toString() {
                        return invoker.toString();
                    }
                };
            }
        }
    // 創建一個回調的 Invoker,如果定義的Filter是 ListenableFilter的實現類
        // 那麼調用返回時,會回調Filter.Listener的 onResponse(正常返回) 和 onError方法(異常情況)
        return new CallbackRegistrationInvoker<>(last, filters);
    }

// ProtocolListenerWrapper的refer方法
    @Override
    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
        if (REGISTRY_PROTOCOL.equals(url.getProtocol())) {// protocol = dubbo,此處不成立
            return protocol.refer(type, url);
        }
        // 此處的 protocol是真實的 DubboProtocol
        // 調用 DubboProtocol的refer 之後生成 DubboInvoker
        // 獲取 InvokerListener的激活擴展點實現,若是存在,則調用擴展實現的 referred(Invoker<?> invoker)方法,(類似Spring中的BeanPostProcessor)
        // 返回 ListenerInvokerWrapper
        return new ListenerInvokerWrapper<T>(protocol.refer(type, url),
                Collections.unmodifiableList(
                        ExtensionLoader.getExtensionLoader(InvokerListener.class)
                                .getActivateExtension(url, INVOKER_LISTENER_KEY)));
    }


// AbstractProtocol (DubboProtocol的父類)
    @Override
    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
        // protocolBindingRefer 方法是個抽象方法,讓其子類去實現
        return new AsyncToSyncInvoker<>(protocolBindingRefer(type, url));
    }


// DubboProtocol
    @Override
    public <T> Invoker<T> protocolBindingRefer(Class<T> serviceType, URL url) throws RpcException {
        optimizeSerialization(url);

        // create rpc invoker.
        DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers);
        invokers.add(invoker);

        return invoker;
    }

    private ExchangeClient[] getClients(URL url) {
        
        // whether to share connection
        boolean useShareConnect = false;

        int connections = url.getParameter(CONNECTIONS_KEY, 0);
        List<ReferenceCountExchangeClient> shareClients = null;
        // if not configured, connection is shared, otherwise, one connection for one service
        // 沒有配置 connections 參數的話,默認使用共享連接(一個服務公用一個連接) 
        if (connections == 0) {
            useShareConnect = true;

            /**
             * The xml configuration should have a higher priority than properties.
             */
            String shareConnectionsStr = url.getParameter(SHARE_CONNECTIONS_KEY, (String) null);
            connections = Integer.parseInt(StringUtils.isBlank(shareConnectionsStr) ? ConfigUtils.getProperty(SHARE_CONNECTIONS_KEY,
                    DEFAULT_SHARE_CONNECTIONS) : shareConnectionsStr);
            
            // 獲取共享客戶端連接
            shareClients = getSharedClient(url, connections);
        }
        // 根據連接數創建請求客戶端數組(ReferenceCountExchangeClient)
        ExchangeClient[] clients = new ExchangeClient[connections];
        for (int i = 0; i < clients.length; i++) {
            // 使用共享連接
            if (useShareConnect) {
                clients[i] = shareClients.get(i); // shareconnections 優先級高於 connections

            } else {
                clients[i] = initClient(url); // 初始化客戶端連接 HeaderExchangeClient
            }
        }

        return clients;
    }

    private ReferenceCountExchangeClient buildReferenceCountExchangeClient(URL url) {
        // 檢查完緩存中沒有的話,也會調用此方法去創建客戶端連接
        ExchangeClient exchangeClient = initClient(url);

        return new ReferenceCountExchangeClient(exchangeClient);
    }

    private ExchangeClient initClient(URL url) {

        // client type setting.
        String str = url.getParameter(CLIENT_KEY, url.getParameter(SERVER_KEY, DEFAULT_REMOTING_CLIENT));
        // 設置默認的編解碼爲 codec = dubbo(DubboCodec)
        url = url.addParameter(CODEC_KEY, DubboCodec.NAME);
        // enable heartbeat by default(1分鐘)
        url = url.addParameterIfAbsent(HEARTBEAT_KEY, String.valueOf(DEFAULT_HEARTBEAT));

        // BIO is not allowed since it has severe performance issue.
        if (str != null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str)) {
            throw new RpcException("Unsupported client type: " + str + "," +
                    " supported client type is " + StringUtils.join(ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions(), " "));
        }

        ExchangeClient client;
        try {
            // connection should be lazy
            if (url.getParameter(LAZY_CONNECT_KEY, false)) {
                client = new LazyConnectExchangeClient(url, requestHandler);

            } else {
                // 通過交換層 Exchangers工具類 生成客戶端
                client = Exchangers.connect(url, requestHandler);
            }

        } catch (RemotingException e) {
            throw new RpcException("Fail to create remoting client for service(" + url + "): " + e.getMessage(), e);
        }

        return client;
    }

// 中間的代碼就不一一粘貼了,下面來看一下創建NettyClient時關鍵性的一步:

    /**
     * The constructor of NettyClient.
     * It wil init and start netty.
     */
    public NettyClient(final URL url, final ChannelHandler handler) throws RemotingException {
        // 調用父類構造器
        super(url, wrapChannelHandler(url, handler));
    }

// AbstractClient 的構造器,有幾處模板方法:doOpen()、connect()、close()
    public AbstractClient(URL url, ChannelHandler handler) throws RemotingException {
        super(url, handler);

        needReconnect = url.getParameter(Constants.SEND_RECONNECT_KEY, false);

        try {
            // 打開連接通道
            doOpen();
        } catch (Throwable t) {
            close();
            ...
        }
        try {
            // connect.
            connect();
            ...
            }
        } catch (RemotingException t) {
            if (url.getParameter(Constants.CHECK_KEY, true)) {
                close(); // 異常關閉連接
                throw t;
            } else {
                ...
            }
        } catch (Throwable t) {
            close(); // 異常關閉連接
            ...
        }

        ...
    }

// NettyClient(注意此處說的這個類都是Dubbo自己的,Netty裏面可沒有叫這玩意的)抽象實現
    @Override
    protected void doOpen() throws Throwable {
        // 創建一個統一出入站處理器(爲啥是出入? 因爲實現了 io.netty.channel.ChannelDuplexHandler)
        final NettyClientHandler nettyClientHandler = new NettyClientHandler(getUrl(), this);
        // 創建客戶端啓動類
        bootstrap = new Bootstrap();
        // nioEventLoopGroup 是一個線程數爲 CPU+1的 NioEventLoopGroup(事件循環組,可以當成線程池)
        bootstrap.group(nioEventLoopGroup)
                .option(ChannelOption.SO_KEEPALIVE, true)
                .option(ChannelOption.TCP_NODELAY, true)
                .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
                //.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, getTimeout()) 
                // io.netty.channel.socket.nio.NioSocketChannel中會緩存一個 SocketChannel 用於與服務端進行通信
                .channel(NioSocketChannel.class); 

        bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, Math.max(3000, getConnectTimeout()));
        bootstrap.handler(new ChannelInitializer() {

            @Override
            protected void initChannel(Channel ch) throws Exception {
                int heartbeatInterval = UrlUtils.getHeartbeat(getUrl());
                // 自定義編解碼器(粘包拆包)
                NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyClient.this);
                ch.pipeline()
                    // 可以參考 io.netty.channel.ChannelPipeline 中的註釋說明
                    // 讀:decoder -> client-idle-handler -> handler
                    // 寫:handler -> client-idle-handler -> encoder
                        .addLast("decoder", adapter.getDecoder())
                        .addLast("encoder", adapter.getEncoder())
                        .addLast("client-idle-handler", new IdleStateHandler(heartbeatInterval, 0, 0, MILLISECONDS))
                        .addLast("handler", nettyClientHandler);
                String socksProxyHost = ConfigUtils.getProperty(SOCKS_PROXY_HOST);
                if(socksProxyHost != null) {
                    int socksProxyPort = Integer.parseInt(ConfigUtils.getProperty(SOCKS_PROXY_PORT, DEFAULT_SOCKS_PROXY_PORT));
                    Socks5ProxyHandler socks5ProxyHandler = new Socks5ProxyHandler(new InetSocketAddress(socksProxyHost, socksProxyPort));
                    ch.pipeline().addFirst(socks5ProxyHandler);
                }
            }
        });
    }

// NettyClient 抽象實現
    @Override
    protected void doConnect() throws Throwable {
        long start = System.currentTimeMillis();
        // 通過調用netty 封裝好的客戶端與服務端進行連接,此連接會立馬返回一個 ChannelFuture(DefaultChannelPromise)。
        // 類似於 JUC 中的 Future
        ChannelFuture future = bootstrap.connect(getConnectAddress());
        try {
            // 根據超時時間不中斷獲取連接結果
            boolean ret = future.awaitUninterruptibly(getConnectTimeout(), MILLISECONDS);
            // 連接成功
            if (ret && future.isSuccess()) {
                Channel newChannel = future.channel();
                try {
                    // Close old channel
                    // copy reference
                    Channel oldChannel = NettyClient.this.channel;
                    if (oldChannel != null) {
                        try {
                            if (logger.isInfoEnabled()) {
                                logger.info("Close old netty channel " + oldChannel + " on create new netty channel " + newChannel);
                            }
                            oldChannel.close();
                        } finally {
                            NettyChannel.removeChannelIfDisconnected(oldChannel);
                        }
                    }
                } finally {
                    if (NettyClient.this.isClosed()) {
                        try {
                            if (logger.isInfoEnabled()) {
                                logger.info("Close new netty channel " + newChannel + ", because the client closed.");
                            }
                            newChannel.close();
                        } finally {
                            NettyClient.this.channel = null;
                            NettyChannel.removeChannelIfDisconnected(newChannel);
                        }
                    } else {
                        NettyClient.this.channel = newChannel;
                    }
                }
            } else if (future.cause() != null) {
                ...
        } finally {
            // just add new valid channel to NettyChannel's cache
            if (!isConnected()) {
                //future.cancel(true);
            }
        }
    }

關鍵流程

  1. Invoker鏈創建過程:
    ① DubboInvoker -> 真正發送請求的Invoker
    ② AsyncToSyncInvoker(DubboInvoker ** ) ->
    ③ ListenerInvokerWrapper( AsyncToSyncInvoker ) ->
    ④ CallbackRegistrationInvoker( ListenerInvokerWrapper ) -> Filter擴展點鉤子回調
    ⑤ InvokerDelegate(
    CallbackRegistrationInvoker) ->
    ⑥ FailoverClusterInvoker( RegistryDirectory(
    InvokerDelegates) ) -> RegistryDirectory 中存放調用時的Invokes
    ⑦ MockClusterInvoker (
    RegistryDirectory,FailoverClusterInvoker**) 暴漏給接口攔截的入口Invoker
  • PS:上面是創建順序,調用時自然是相反的順序。那麼如何方便記憶呢 ?
    Ⅰ 最後一個Invoker,實際發出網絡請求的Invoker:DubboInvoker,鐵打不變 !!!
    Ⅱ 由於 DubboInvoker 是異步返回,若是客戶端指定同步調用(默認同步),則需要在AsyncToSyncInvoker(最靠近DubboInvoker)
    Ⅲ CallbackRegistrationInvoker,對返回結果的第一層處理。通過定義的Filter類型決定是否調用 onResponse方法 或者 onError方法
    Ⅳ Mock優先
    Ⅴ 高可用高於任何其他的執行Invoker( 故障轉移:FailoverClusterInvoker
  1. Netty客戶端創建過程:
    org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol#requestHandler -> 真正處理消息的收發處理器
    HeaderExchangeHandler(requestHandler) ->
    DecodeHandler(HeaderExchangeHandler) -> 自定義編解碼處理器(拆包、粘包)
    AllChannelHandler(DecodeHandler,URL) -> 請求分發處理器,默認所有請求都轉發
    HeartbeatHandler(AllChannelHandler) ->
    MultiMessageHandler (HeartbeatHandler) -> 消息類型是 MultiMessage,不過一般的請求都是 Request類型
    NettyClient(Url,MultiMessageHandler) -> 封裝了一系列打開、鏈接通道邏輯
    HeaderExchangeClient(NettyClient,true) ->
    ReferenceCountExchangeClient(HeaderExchangeClient) 共享連接纔會創建 ReferenceCountExchangeClient,暴漏給客戶端使用的Client

8這一步是通過如下兩行代碼:

// 設置cluster = registryaware( RegistryAwareCluster )
URL u = registryURL.addParameter(CLUSTER_KEY, RegistryAwareCluster.NAME);
// 通過集羣的自適應擴展點將多個Invokers( MockClusterInvoker 集合)合併成一個Invoker(RegistryAwareClusterInvoker是用來做集羣路由,選擇一個實際調用的Invoker),此處RegistryAwareClusterInvoker是用來做集羣路由,選擇一個實際調用的Invoker
invoker =  CLUSTER.join(new StaticDirectory(u, invokers))

到這裏,大家千萬不要被 "滿眼的Invoker" 給繞暈了,仔細分析一下Dubbo的執行鏈與模塊分層是可以理清楚的
接着上面 執行鏈,此處會先包裝一個StaticDirectory,然後 :
MockClusterWrapper(MockClusterInvoker )-> RegistryAwareCluster(RegistryAwareClusterInvoker, 最終返回的Invoker)-> StaticDirectory (URL , List<MockClusterInvoker>)
StaticDirectory:通過前面設置的Router鏈來執行路由選擇,會選擇最後一個Router所路由到的List<Invoker>集合上
LoadBalance:負載均衡器(默認random,隨機),從前面選擇到的List<Invoker>集合上,根據負載均衡算法選擇一個
路由跟負載均衡都是調用invoke方法時纔會用到,我們初始化過程分析完之後,調用過程自然就清晰了

9步驟有以下核心子流程:
① 接着就開始創建代理引用了: 通過調用 ProxyFactory擴展點的適配類的getProxy方法獲取代理對象:

// ProxyFactory$Adaptive.getProxy(invoker) -> 找到默認的擴展點名稱(javassist)-> JavassistProxyFactory(實際是父類 AbstractProxyFactory的 getProxy方法)
return (T) PROXY_FACTORY.getProxy(invoker)

// AbstractProxyFactory
    @Override
    public <T> T getProxy(Invoker<T> invoker) throws RpcException {
        // 非泛化方式獲取接口代理對象
        return getProxy(invoker, false);
    }
    
// JavassistProxyFactory
    @Override
    @SuppressWarnings("unchecked")
    public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
        // 此處的創建代理的方式類似於 JDK動態代理,不過生成的class是藉助與 javassist 字節碼工具自定義的
        // 而且生成的代理類繼承了org.apache.dubbo.common.bytecode.Proxy
        return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
    }

② 若不指定 interfaces這個key的話,生成的代理類會同時實現interfaceEchoService(用於回聲測試)接口,具體代碼 Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker)) 。其中 Proxy.getProxy(interfaces) 會爲接口生成一個代理類,代理繼承了 org.apache.dubbo.common.bytecode.Proxy 抽象類並且實現了 interfaces生成的代理類如下

public class com.alibaba.dubbo.common.bytecode.proxy0  extends com.alibaba.dubbo.common.bytecode.Proxy implements com.alibaba.dubbo.common.bytecode.ClassGenerator$DC, com.alibaba.dubbo.rpc.service.EchoService, com.alibaba.mp.alipay.AlipayInfoAbility {


      public static java.lang.reflect.Method[] methods;
      private java.lang.reflect.InvocationHandler handler;


      public (java.lang.reflect.InvocationHandler arg0){ 

          handler = $1;
      }      

      // 代理的接口方法
      public com.alibaba.soa.common.response.SoaResponse getAlipayInfo(GetAlipayInfoRequest arg0){

            Object[] args = new Object[1]; 

            args[0] = ($w)$1; 
            // 調用handler的目標方法,此處是 InvokerInvocationHandler
            Object ret = handler.invoke(this, methods[0], args); 

            return (com.alibaba.soa.common.response.SoaResponse)ret;
      }


      // 默認都會實現 EchoSerice接口,用於回聲測試
      public java.lang.Object $echo(java.lang.Object arg0) {

            Object[] args = new Object[1];

            args[0] = ($w)$1;

            Object ret = handler.invoke(this, methods[3], args);

            return (java.lang.Object)ret;
      }

      // 創建代理對象
      public Object newInstance(java.lang.reflect.InvocationHandler h) {

            return new com.alibaba.dubbo.common.bytecode.proxy0($1); 
      }

③ 看了上述動態生成代理類的過程,再來看 Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker)) 這個操作就會很清晰了。拿上面的實現方法來看,當調用ReferenceConcig.get()獲取的對象就是Proxy0。調用接口的getAlipayInfo方法,實際上調用的是 Proxy0getAlipayInfo方法,此代理中,會調用handler的 invoke方法,再來看一下 InvokerInvocationHandler中的實現邏輯。
④ 正常情況下,我們使用JDK動態代理的時候纔會實現java.lang.reflect.InvocationHandler,此處完全可以不實現InvocationHandler。但是由於代理的本質是相同的,作者爲了代碼邏輯複用,統一處理了。(PS: 最後這句是我猜的 😁

public class InvokerInvocationHandler implements InvocationHandler {

    private final Invoker<?> invoker; // RegistryAwareClusterInvoker

    public InvokerInvocationHandler(Invoker<?> handler) {
        this.invoker = handler;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String methodName = method.getName();
        Class<?>[] parameterTypes = method.getParameterTypes();
        if (method.getDeclaringClass() == Object.class) {
            return method.invoke(invoker, args);
        }
        if ("toString".equals(methodName) && parameterTypes.length == 0) {
            return invoker.toString();
        }
        if ("hashCode".equals(methodName) && parameterTypes.length == 0) {
            return invoker.hashCode();
        }
        if ("equals".equals(methodName) && parameterTypes.length == 1) {
            return invoker.equals(args[0]);
        }

        // 此處終於開始調用了。。。
        return invoker.invoke(new RpcInvocation(method, args)).recreate();
    }

}
  • 到此,客戶端的初始化Invoker過程已經完畢,後面就是真正開始調用的過程,下一篇我們來分析客戶端的調用過程與Netty底層源碼解讀。
  1. 文章要是勘誤或者知識點說的不正確,歡迎評論與交流, 希望學習的道路不孤獨。由於文章是作者通過閱讀源碼獲得的知識,難免會有疏忽!
  2. 要是感覺文章對你有所幫助,不妨點個關注,或者移駕看一下作者的其他文集,也都是幹活多多哦,文章也在全力更新中。
  3. 著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章