国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

dubbo源碼解析(四十五)服務引用過程

xiaowugui666 / 2059人閱讀

摘要:服務引用過程目標從源碼的角度分析服務引用過程。并保留服務提供者的部分配置,比如版本,,時間戳等最后將合并后的配置設置為查詢字符串中。的可以參考源碼解析二十三遠程調用的一的源碼分析。

dubbo服務引用過程
目標:從源碼的角度分析服務引用過程。
前言

前面服務暴露過程的文章講解到,服務引用有兩種方式,一種就是直連,也就是直接指定服務的地址來進行引用,這種方式更多的時候被用來做服務測試,不建議在生產環境使用這樣的方法,因為直連不適合服務治理,dubbo本身就是一個服務治理的框架,提供了很多服務治理的功能。所以更多的時候,我們都不會選擇繞過注冊中心,而是通過注冊中心的方式來進行服務引用。

服務引用過程

大致可以分為三個步驟:

配置加載

創建invoker

創建服務接口代理類

引用起點

dubbo服務的引用起點就類似于bean加載。dubbo中有一個類ReferenceBean,它實現了FactoryBean接口,繼承了ReferenceConfig,所以ReferenceBean作為dubbo中能生產對象的工廠Bean,而我們要引用服務,也就是要有一個該服務的對象。

服務引用被觸發有兩個時機:

Spring 容器調用 ReferenceBean 的 afterPropertiesSet 方法時引用服務(餓漢式)

在 ReferenceBean 對應的服務被注入到其他類中時引用(懶漢式)

默認情況下,Dubbo 使用懶漢式引用服務。如果需要使用餓漢式,可通過配置 的 init 屬性開啟。

因為ReferenceBean實現了FactoryBean接口的getObject()方法,所以在加載bean的時候,會調用ReferenceBean的getObject()方法

ReferenceBean的getObject()
public Object getObject() {
    return get();
}

這個get方法是ReferenceConfig的get()方法

ReferenceConfig的get()
public synchronized T get() {
    // 檢查并且更新配置
    checkAndUpdateSubConfigs();

    // 如果被銷毀,則拋出異常
    if (destroyed) {
        throw new IllegalStateException("The invoker of ReferenceConfig(" + url + ") has already destroyed!");
    }
    // 檢測 代理對象ref 是否為空,為空則通過 init 方法創建
    if (ref == null) {
        // 用于處理配置,以及調用 createProxy 生成代理類
        init();
    }
    return ref;
}

關于checkAndUpdateSubConfigs()方法前一篇文章已經講了,我就不再講述。這里關注init方法。該方法也是處理各類配置的開始。

配置加載(1)
ReferenceConfig的init()
private void init() {
    // 如果已經初始化過,則結束
    if (initialized) {
        return;
    }
    // 設置初始化標志為true
    initialized = true;
    // 本地存根合法性校驗
    checkStubAndLocal(interfaceClass);
    // mock合法性校驗
    checkMock(interfaceClass);
    // 用來存放配置
    Map map = new HashMap();

    // 存放這是消費者側
    map.put(Constants.SIDE_KEY, Constants.CONSUMER_SIDE);

    // 添加 協議版本、發布版本,時間戳 等信息到 map 中
    appendRuntimeParameters(map);
    // 如果是泛化調用
    if (!isGeneric()) {
        // 獲得版本號
        String revision = Version.getVersion(interfaceClass, version);
        if (revision != null && revision.length() > 0) {
            // 設置版本號
            map.put(Constants.REVISION_KEY, revision);
        }

        // 獲得所有方法
        String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();
        if (methods.length == 0) {
            logger.warn("No method found in service interface " + interfaceClass.getName());
            map.put(Constants.METHODS_KEY, Constants.ANY_VALUE);
        } else {
            // 把所有方法簽名拼接起來放入map
            map.put(Constants.METHODS_KEY, StringUtils.join(new HashSet(Arrays.asList(methods)), Constants.COMMA_SEPARATOR));
        }
    }
    // 加入服務接口名稱
    map.put(Constants.INTERFACE_KEY, interfaceName);
    // 添加metrics、application、module、consumer、protocol的所有信息到map
    appendParameters(map, metrics);
    appendParameters(map, application);
    appendParameters(map, module);
    appendParameters(map, consumer, Constants.DEFAULT_KEY);
    appendParameters(map, this);
    Map attributes = null;
    if (CollectionUtils.isNotEmpty(methods)) {
        attributes = new HashMap();
        // 遍歷方法配置
        for (MethodConfig methodConfig : methods) {
            // 把方法配置加入map
            appendParameters(map, methodConfig, methodConfig.getName());
            // 生成重試的配置key
            String retryKey = methodConfig.getName() + ".retry";
            // 如果map中已經有該配置,則移除該配置
            if (map.containsKey(retryKey)) {
                String retryValue = map.remove(retryKey);
                // 如果配置為false,也就是不重試,則設置重試次數為0次
                if ("false".equals(retryValue)) {
                    map.put(methodConfig.getName() + ".retries", "0");
                }
            }
            // 設置異步配置
            attributes.put(methodConfig.getName(), convertMethodConfig2AyncInfo(methodConfig));
        }
    }

    // 獲取服務消費者 ip 地址
    String hostToRegistry = ConfigUtils.getSystemProperty(Constants.DUBBO_IP_TO_REGISTRY);
    // 如果為空,則獲取本地ip
    if (StringUtils.isEmpty(hostToRegistry)) {
        hostToRegistry = NetUtils.getLocalHost();
    }
    // 設置消費者ip
    map.put(Constants.REGISTER_IP_KEY, hostToRegistry);

    // 創建代理對象
    ref = createProxy(map);

    // 生產服務key
    String serviceKey = URL.buildKey(interfaceName, group, version);
    // 根據服務名,ReferenceConfig,代理類構建 ConsumerModel,
    // 并將 ConsumerModel 存入到 ApplicationModel 中
    ApplicationModel.initConsumerModel(serviceKey, buildConsumerModel(serviceKey, attributes));
}

該方法大致分為以下幾個步驟:

檢測本地存根和mock合法性。

添加協議版本、發布版本,時間戳、metrics、application、module、consumer、protocol等的所有信息到 map 中

多帶帶處理方法配置,設置重試次數配置以及設置該方法對異步配置信息。

添加消費者ip地址到map

創建代理對象

生成ConsumerModel存入到 ApplicationModel 中

在這里處理配置到邏輯比較清晰。下面就是看ReferenceConfig的createProxy()方法。

創建invoker
ReferenceConfig的createProxy()
private T createProxy(Map map) {
    // 根據配置檢查是否為本地調用
    if (shouldJvmRefer(map)) {
        // 生成url,protocol使用的是injvm
        URL url = new URL(Constants.LOCAL_PROTOCOL, Constants.LOCALHOST_VALUE, 0, interfaceClass.getName()).addParameters(map);
        // 利用InjvmProtocol 的 refer 方法生成 InjvmInvoker 實例
        invoker = refprotocol.refer(interfaceClass, url);
        if (logger.isInfoEnabled()) {
            logger.info("Using injvm service " + interfaceClass.getName());
        }
    } else {
        // 如果url不為空,則用戶可能想進行直連來調用
        if (url != null && url.length() > 0) { // user specified URL, could be peer-to-peer address, or register center"s address.
            // 當需要配置多個 url 時,可用分號進行分割,這里會進行切分
            String[] us = Constants.SEMICOLON_SPLIT_PATTERN.split(url);
            // 遍歷所有的url
            if (us != null && us.length > 0) {
                for (String u : us) {
                    URL url = URL.valueOf(u);
                    if (StringUtils.isEmpty(url.getPath())) {
                        // 設置接口全限定名為 url 路徑
                        url = url.setPath(interfaceName);
                    }
                    // 檢測 url 協議是否為 registry,若是,表明用戶想使用指定的注冊中心
                    if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
                        // 將 map 轉換為查詢字符串,并作為 refer 參數的值添加到 url 中
                        urls.add(url.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
                    } else {
                        // 合并 url,移除服務提供者的一些配置(這些配置來源于用戶配置的 url 屬性),
                        // 比如線程池相關配置。并保留服務提供者的部分配置,比如版本,group,時間戳等
                        // 最后將合并后的配置設置為 url 查詢字符串中。
                        urls.add(ClusterUtils.mergeUrl(url, map));
                    }
                }
            }
        } else { // assemble URL from register center"s configuration
            // 校驗注冊中心
            checkRegistry();
            // 加載注冊中心的url
            List us = loadRegistries(false);
            if (CollectionUtils.isNotEmpty(us)) {
                // 遍歷所有的注冊中心
                for (URL u : us) {
                    // 生成監控url
                    URL monitorUrl = loadMonitor(u);
                    if (monitorUrl != null) {
                        // 加入監控中心url的配置
                        map.put(Constants.MONITOR_KEY, URL.encode(monitorUrl.toFullString()));
                    }
                    // 添加 refer 參數到 url 中,并將 url 添加到 urls 中
                    urls.add(u.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
                }
            }
            // 如果urls為空,則拋出異常
            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  to your spring config.");
            }
        }

        // 如果只有一個注冊中心,則直接調用refer方法
        if (urls.size() == 1) {
            // 調用 RegistryProtocol 的 refer 構建 Invoker 實例
            invoker = refprotocol.refer(interfaceClass, urls.get(0));
        } else {
            List> invokers = new ArrayList>();
            URL registryURL = null;
            // 遍歷所有的注冊中心url
            for (URL url : urls) {
                // 通過 refprotocol 調用 refer 構建 Invoker,
                // refprotocol 會在運行時根據 url 協議頭加載指定的 Protocol 實例,并調用實例的 refer 方法
                // 把生成的Invoker加入到集合中
                invokers.add(refprotocol.refer(interfaceClass, url));
                // 如果是注冊中心的協議
                if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
                    // 則設置registryURL
                    registryURL = url; // use last registry url
                }
            }
            // 優先用注冊中心的url
            if (registryURL != null) { // registry url is available
                // use RegistryAwareCluster only when register"s cluster is available
                // 只有當注冊中心當鏈接可用當時候,采用RegistryAwareCluster
                URL u = registryURL.addParameter(Constants.CLUSTER_KEY, RegistryAwareCluster.NAME);
                // The invoker wrap relation would be: RegistryAwareClusterInvoker(StaticDirectory) -> FailoverClusterInvoker(RegistryDirectory, will execute route) -> Invoker
                // 由集群進行多個invoker合并
                invoker = cluster.join(new StaticDirectory(u, invokers));
            } else { // not a registry url, must be direct invoke.
                // 直接進行合并
                invoker = cluster.join(new StaticDirectory(invokers));
            }
        }
    }

    // 如果需要核對該服務是否可用,并且該服務不可用
    if (shouldCheck() && !invoker.isAvailable()) {
        // make it possible for consumer to retry later if provider is temporarily unavailable
        // 修改初始化標志為false
        initialized = false;
        // 拋出異常
        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
        URL consumerURL = new URL(Constants.CONSUMER_PROTOCOL, map.remove(Constants.REGISTER_IP_KEY), 0, map.get(Constants.INTERFACE_KEY), map);
        // 把消費者配置加入到元數據中心中
        metadataReportService.publishConsumer(consumerURL);
    }
    // create service proxy
    // 創建服務代理
    return (T) proxyFactory.getProxy(invoker);
}

該方法的大致邏輯可用分為以下幾步:

如果是本地調用,則直接使用InjvmProtocol 的 refer 方法生成 Invoker 實例。

如果不是本地調用,但是是選擇直連的方式來進行調用,則分割配置的多個url。如果協議是配置是registry,則表明用戶想使用指定的注冊中心,配置url后將url并且保存到urls里面,否則就合并url,并且保存到urls。

如果是通過注冊中心來進行調用,則先校驗所有的注冊中心,然后加載注冊中心的url,遍歷每個url,加入監控中心url配置,最后把每個url保存到urls。

針對urls集合的數量,如果是單注冊中心,直接引用RegistryProtocol 的 refer 構建 Invoker 實例,如果是多注冊中心,則對每個url都生成Invoker,利用集群進行多個Invoker合并。

最終輸出一個invoker。

Invoker 是 Dubbo 的核心模型,代表一個可執行體。在服務提供方,Invoker 用于調用服務提供類。在服務消費方,Invoker 用于執行遠程調用。Invoker 是由 Protocol 實現類構建而來。關于這幾個接口的定義介紹可以參考《dubbo源碼解析(十九)遠程調用——開篇》,Protocol 實現類有很多,下面會分析 RegistryProtocol 和 DubboProtocol,我們可以看到上面的源碼中講到,當只有一個注冊中心的時候,會直接使用RegistryProtocol。所以先來看看RegistryProtocol的refer()方法。

RegistryProtocol生成invoker
RegistryProtocol的refer()
public  Invoker refer(Class type, URL url) throws RpcException {
    // 取 registry 參數值,并將其設置為協議頭,默認是dubbo
    url = URLBuilder.from(url)
            .setProtocol(url.getParameter(REGISTRY_KEY, DEFAULT_REGISTRY))
            .removeParameter(REGISTRY_KEY)
            .build();
    // 獲得注冊中心實例
    Registry registry = registryFactory.getRegistry(url);
    // 如果是注冊中心服務,則返回注冊中心服務的invoker
    if (RegistryService.class.equals(type)) {
        return proxyFactory.getInvoker((T) registry, type, url);
    }

    // group="a,b" or group="*"
    // 將 url 查詢字符串轉為 Map
    Map qs = StringUtils.parseQueryString(url.getParameterAndDecoded(REFER_KEY));
    // 獲得group值
    String group = qs.get(Constants.GROUP_KEY);
    if (group != null && group.length() > 0) {
        // 如果有多個組,或者組配置為*,則使用MergeableCluster,并調用 doRefer 繼續執行服務引用邏輯
        if ((COMMA_SPLIT_PATTERN.split(group)).length > 1 || "*".equals(group)) {
            return doRefer(getMergeableCluster(), registry, type, url);
        }
    }
    // 只有一個組或者沒有組配置,則直接執行doRefer
    return doRefer(cluster, registry, type, url);
}

上面的邏輯比較簡單,如果是注冊服務中心,則直接創建代理。如果不是,先處理組配置,根據組配置來決定Cluster的實現方式,然后調用doRefer方法。

RegistryProtocol的doRefer()
private  Invoker doRefer(Cluster cluster, Registry registry, Class type, URL url) {
    // 創建 RegistryDirectory 實例
    RegistryDirectory directory = new RegistryDirectory(type, url);
    // 設置注冊中心
    directory.setRegistry(registry);
    // 設置協議
    directory.setProtocol(protocol);
    // all attributes of REFER_KEY
    // 所有屬性放到map中
    Map parameters = new HashMap(directory.getUrl().getParameters());
    // 生成服務消費者鏈接
    URL subscribeUrl = new URL(CONSUMER_PROTOCOL, parameters.remove(REGISTER_IP_KEY), 0, type.getName(), parameters);
    // 注冊服務消費者,在 consumers 目錄下新節點
    if (!ANY_VALUE.equals(url.getServiceInterface()) && url.getParameter(REGISTER_KEY, true)) {
        directory.setRegisteredConsumerUrl(getRegisteredConsumerUrl(subscribeUrl, url));
        // 注冊服務消費者
        registry.register(directory.getRegisteredConsumerUrl());
    }
    // 創建路由規則鏈
    directory.buildRouterChain(subscribeUrl);
    // 訂閱 providers、configurators、routers 等節點數據
    directory.subscribe(subscribeUrl.addParameter(CATEGORY_KEY,
            PROVIDERS_CATEGORY + "," + CONFIGURATORS_CATEGORY + "," + ROUTERS_CATEGORY));
    // 一個注冊中心可能有多個服務提供者,因此這里需要將多個服務提供者合并為一個,生成一個invoker
    Invoker invoker = cluster.join(directory);
    // 在服務提供者處注冊消費者
    ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory);
    return invoker;
}

該方法大致可以分為以下步驟:

創建一個 RegistryDirectory 實例,然后生成服務者消費者鏈接。

向注冊中心進行注冊。

緊接著訂閱 providers、configurators、routers 等節點下的數據。完成訂閱后,RegistryDirectory 會收到這幾個節點下的子節點信息。

由于一個服務可能部署在多臺服務器上,這樣就會在 providers 產生多個節點,這個時候就需要 Cluster 將多個服務節點合并為一個,并生成一個 Invoker。關于 RegistryDirectory 和 Cluster,可以看我前面寫的一些文章介紹。

DubboProtocol生成invoker

首先還是從DubboProtocol的refer()開始。

DubboProtocol的refer()
public  Invoker refer(Class serviceType, URL url) throws RpcException {
    optimizeSerialization(url);

    // create rpc invoker.
    // 創建一個DubboInvoker實例
    DubboInvoker invoker = new DubboInvoker(serviceType, url, getClients(url), invokers);
    // 加入到集合中
    invokers.add(invoker);

    return invoker;
}

創建DubboInvoker比較簡單,調用了構造方法,這里主要講這么生成ExchangeClient,也就是getClients方法。

DubboProtocol的getClients()

可以參考《dubbo源碼解析(二十四)遠程調用——dubbo協議》的(三)DubboProtocol中的源碼分析。最新版本基本沒有什么變化,只是因為加入了配置中心,配置的優先級更加明確了,所以增加了xml配置優先級高于properties配置的代碼邏輯,都比較容易理解。

其中如果是配置的共享,則獲得共享客戶端對象,也就是getSharedClient()方法,否則新建客戶端也就是initClient()方法。

DubboProtocol的getSharedClient()

可以參考《dubbo源碼解析(二十四)遠程調用——dubbo協議》的(三)DubboProtocol中的源碼分析,該方法比較簡單,先訪問緩存,若緩存未命中,則通過 initClient 方法創建新的 ExchangeClient 實例,并將該實例傳給 ReferenceCountExchangeClient 構造方法創建一個帶有引用計數功能的 ExchangeClient 實例。

DubboProtocol的initClient()

可以參考《dubbo源碼解析(二十四)遠程調用——dubbo協議》的(三)DubboProtocol中的源碼分析,initClient 方法首先獲取用戶配置的客戶端類型,最新版本已經改為默認 netty4。然后設置用戶心跳配置,然后檢測用戶配置的客戶端類型是否存在,不存在則拋出異常。最后根據 lazy 配置決定創建什么類型的客戶端。這里的 LazyConnectExchangeClient 代碼并不是很復雜,該類會在 request 方法被調用時通過 Exchangers 的 connect 方法創建 ExchangeClient 客戶端。下面我們分析一下 Exchangers 的 connect 方法。

Exchangers的connect()
public static ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException {
    if (url == null) {
        throw new IllegalArgumentException("url == null");
    }
    if (handler == null) {
        throw new IllegalArgumentException("handler == null");
    }
    url = url.addParameterIfAbsent(Constants.CODEC_KEY, "exchange");
    // 獲取 Exchanger 實例,默認為 HeaderExchangeClient
    return getExchanger(url).connect(url, handler);
}

getExchanger 會通過 SPI 加載 HeaderExchangeClient 實例,這個方法比較簡單。接下來分析 HeaderExchangeClient 的connect的實現。

HeaderExchangeClient 的connect()
public ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException {
    // 創建 HeaderExchangeHandler 對象
    // 創建 DecodeHandler 對象
    // 通過 Transporters 構建 Client 實例
    // 創建 HeaderExchangeClient 對象
    return new HeaderExchangeClient(Transporters.connect(url, new DecodeHandler(new HeaderExchangeHandler(handler))), true);
}

其中HeaderExchangeHandler、DecodeHandler等可以參考《dubbo源碼解析(九)遠程通信——Transport層》和《dubbo源碼解析(十)遠程通信——Exchange層》的分析。這里重點關注Transporters 構建 Client,也就是Transporters的connect方法。

Transporters的connect()

可以參考《dubbo源碼解析(八)遠程通信——開篇》的(十)Transporters中源碼分析。其中獲得自適應拓展類,該類會在運行時根據客戶端類型加載指定的 Transporter 實現類。若用戶未配置客戶端類型,則默認加載 NettyTransporter,并調用該類的 connect 方法。假設是netty4的實現,則執行以下代碼。

public Client connect(URL url, ChannelHandler listener) throws RemotingException {
    return new NettyClient(url, listener);
}

到這里為止,DubboProtocol生成invoker過程也結束了。再回到createProxy方法的最后一句代碼,根據invoker創建服務代理對象。

創建代理

為服務接口生成代理對象。有了代理對象,即可進行遠程調用。首先來看AbstractProxyFactory 的 getProxy()方法。

AbstractProxyFactory 的 getProxy()

可以參考《dubbo源碼解析(二十三)遠程調用——Proxy》的(一)AbstractProxyFactory的源碼分析。可以看到第二個getProxy方法其實就是獲取 interfaces 數組,調用到第三個getProxy方法時,該getProxy是個抽象方法,由子類來實現,我們還是默認它的代理實現方式為Javassist。所以可以看JavassistProxyFactory的getProxy方法。

JavassistProxyFactory的getProxy()
public  T getProxy(Invoker invoker, Class[] interfaces) {
    // 生成 Proxy 子類(Proxy 是抽象類)。并調用 Proxy 子類的 newInstance 方法創建 Proxy 實例
    return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
}

我們重點看Proxy的getProxy方法。

/**
 * Get proxy.
 *
 * @param ics interface class array.
 * @return Proxy instance.
 */
public static Proxy getProxy(Class... ics) {
    // 獲得Proxy的類加載器來進行生成代理類
    return getProxy(ClassHelper.getClassLoader(Proxy.class), ics);
}

/**
 * Get proxy.
 *
 * @param cl  class loader.
 * @param ics interface class array.
 * @return Proxy instance.
 */
public static Proxy getProxy(ClassLoader cl, Class... ics) {
    if (ics.length > Constants.MAX_PROXY_COUNT) {
        throw new IllegalArgumentException("interface limit exceeded");
    }

    StringBuilder sb = new StringBuilder();
    // 遍歷接口列表
    for (int i = 0; i < ics.length; i++) {
        String itf = ics[i].getName();
        // 檢測是否是接口,如果不是,則拋出異常
        if (!ics[i].isInterface()) {
            throw new RuntimeException(itf + " is not a interface.");
        }

        Class tmp = null;
        try {
            // 重新加載接口類
            tmp = Class.forName(itf, false, cl);
        } catch (ClassNotFoundException e) {
        }
        // 檢測接口是否相同,這里 tmp 有可能為空,也就是該接口無法被類加載器加載的。
        if (tmp != ics[i]) {
            throw new IllegalArgumentException(ics[i] + " is not visible from class loader");
        }

        // 拼接接口全限定名,分隔符為 ;
        sb.append(itf).append(";");
    }

    // use interface class name list as key.
    // 使用拼接后的接口名作為 key
    String key = sb.toString();

    // get cache by class loader.
    Map cache;
    // 把該類加載器加到本地緩存
    synchronized (ProxyCacheMap) {
        cache = ProxyCacheMap.computeIfAbsent(cl, k -> new HashMap<>());
    }

    Proxy proxy = null;
    synchronized (cache) {
        do {
            // 從緩存中獲取 Reference 實例
            Object value = cache.get(key);
            if (value instanceof Reference) {
                proxy = (Proxy) ((Reference) value).get();
                if (proxy != null) {
                    return proxy;
                }
            }
            // 并發控制,保證只有一個線程可以進行后續操作
            if (value == PendingGenerationMarker) {
                try {
                    // 其他線程在此處進行等待
                    cache.wait();
                } catch (InterruptedException e) {
                }
            } else {
                // 放置標志位到緩存中,并跳出 while 循環進行后續操作
                cache.put(key, PendingGenerationMarker);
                break;
            }
        }
        while (true);
    }

    long id = PROXY_CLASS_COUNTER.getAndIncrement();
    String pkg = null;
    ClassGenerator ccp = null, ccm = null;
    try {
        // 創建 ClassGenerator 對象
        ccp = ClassGenerator.newInstance(cl);

        Set worked = new HashSet<>();
        List methods = new ArrayList<>();

        for (int i = 0; i < ics.length; i++) {
            // 檢測接口訪問級別是否為 protected 或 privete
            if (!Modifier.isPublic(ics[i].getModifiers())) {
                // 獲取接口包名
                String npkg = ics[i].getPackage().getName();
                if (pkg == null) {
                    pkg = npkg;
                } else {
                    // 非 public 級別的接口必須在同一個包下,否者拋出異常
                    if (!pkg.equals(npkg)) {
                        throw new IllegalArgumentException("non-public interfaces from different packages");
                    }
                }
            }
            // 添加接口到 ClassGenerator 中
            ccp.addInterface(ics[i]);

            // 遍歷接口方法
            for (Method method : ics[i].getMethods()) {
                // 獲取方法描述,可理解為方法簽名
                String desc = ReflectUtils.getDesc(method);
                // 如果方法描述字符串已在 worked 中,則忽略。考慮這種情況,
                // A 接口和 B 接口中包含一個完全相同的方法
                if (worked.contains(desc)) {
                    continue;
                }
                worked.add(desc);

                int ix = methods.size();
                // 獲取方法返回值類型
                Class rt = method.getReturnType();
                // 獲取參數列表
                Class[] pts = method.getParameterTypes();

                // 生成 Object[] args = new Object[1...N]
                StringBuilder code = new StringBuilder("Object[] args = new Object[").append(pts.length).append("];");
                for (int j = 0; j < pts.length; j++) {
                    // 生成 args[1...N] = ($w)$1...N;
                    code.append(" args[").append(j).append("] = ($w)$").append(j + 1).append(";");
                }
                // 生成 InvokerHandler 接口的 invoker 方法調用語句,如下:
                // Object ret = handler.invoke(this, methods[1...N], args);
                code.append(" Object ret = handler.invoke(this, methods[").append(ix).append("], args);");
                // 返回值不為 void
                if (!Void.TYPE.equals(rt)) {
                    // 生成返回語句,形如 return (java.lang.String) ret;
                    code.append(" return ").append(asArgument(rt, "ret")).append(";");
                }

                methods.add(method);
                // 添加方法名、訪問控制符、參數列表、方法代碼等信息到 ClassGenerator 中
                ccp.addMethod(method.getName(), method.getModifiers(), rt, pts, method.getExceptionTypes(), code.toString());
            }
        }

        if (pkg == null) {
            pkg = PACKAGE_NAME;
        }

        // create ProxyInstance class.
        // 構建接口代理類名稱:pkg + ".proxy" + id,比如 org.apache.dubbo.proxy0
        String pcn = pkg + ".proxy" + id;
        ccp.setClassName(pcn);
        ccp.addField("public static java.lang.reflect.Method[] methods;");
        // 生成 private java.lang.reflect.InvocationHandler handler;
        ccp.addField("private " + InvocationHandler.class.getName() + " handler;");
        // 為接口代理類添加帶有 InvocationHandler 參數的構造方法,比如:
        // porxy0(java.lang.reflect.InvocationHandler arg0) {
        //     handler=$1;
        // }
        ccp.addConstructor(Modifier.PUBLIC, new Class[]{InvocationHandler.class}, new Class[0], "handler=$1;");
        // 為接口代理類添加默認構造方法
        ccp.addDefaultConstructor();
        // 生成接口代理類
        Class clazz = ccp.toClass();
        clazz.getField("methods").set(null, methods.toArray(new Method[0]));

        // create Proxy class.
        // 構建 Proxy 子類名稱,比如 Proxy1,Proxy2 等
        String fcn = Proxy.class.getName() + id;
        ccm = ClassGenerator.newInstance(cl);
        ccm.setClassName(fcn);
        ccm.addDefaultConstructor();
        ccm.setSuperClass(Proxy.class);
        // 為 Proxy 的抽象方法 newInstance 生成實現代碼,形如:
        // public Object newInstance(java.lang.reflect.InvocationHandler h) {
        //     return new org.apache.dubbo.proxy0($1);
        // }
        ccm.addMethod("public Object newInstance(" + InvocationHandler.class.getName() + " h){ return new " + pcn + "($1); }");
        Class pc = ccm.toClass();
        // 生成 Proxy 實現類
        proxy = (Proxy) pc.newInstance();
    } catch (RuntimeException e) {
        throw e;
    } catch (Exception e) {
        throw new RuntimeException(e.getMessage(), e);
    } finally {
        // release ClassGenerator
        if (ccp != null) {
            // 釋放資源
            ccp.release();
        }
        if (ccm != null) {
            ccm.release();
        }
        synchronized (cache) {
            if (proxy == null) {
                cache.remove(key);
            } else {
                // 寫緩存
                cache.put(key, new WeakReference(proxy));
            }
            // 喚醒其他等待線程
            cache.notifyAll();
        }
    }
    return proxy;
}

代碼比較多,大致可以分為以下幾步:

對接口進行校驗,檢查是否是一個接口,是否不能被類加載器加載。

做并發控制,保證只有一個線程可以進行后續的代理生成操作。

創建cpp,用作為服務接口生成代理類。首先對接口定義以及包信息進行處理。

對接口的方法進行處理,包括返回類型,參數類型等。最后添加方法名、訪問控制符、參數列表、方法代碼等信息到 ClassGenerator 中。

創建接口代理類的信息,比如名稱,默認構造方法等。

生成接口代理類。

創建ccm,ccm 則是用于為 org.apache.dubbo.common.bytecode.Proxy 抽象類生成子類,主要是實現 Proxy 類的抽象方法。

設置名稱、創建構造方法、添加方法

生成 Proxy 實現類。

釋放資源

創建弱引用,寫入緩存,喚醒其他線程。

到這里,接口代理類生成后,服務引用也就結束了。

后記
參考官方文檔:https://dubbo.apache.org/zh-c...

該文章講解了dubbo的服務引用過程,下一篇就講解服務方法調用過程。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/74310.html

相關文章

  • dubbo源碼解析四十八)異步化改造

    摘要:大揭秘異步化改造目標從源碼的角度分析的新特性中對于異步化的改造原理。看源碼解析四十六消費端發送請求過程講到的十四的,在以前的邏輯會直接在方法中根據配置區分同步異步單向調用。改為關于可以參考源碼解析十遠程通信層的六。 2.7大揭秘——異步化改造 目標:從源碼的角度分析2.7的新特性中對于異步化的改造原理。 前言 dubbo中提供了很多類型的協議,關于協議的系列可以查看下面的文章: du...

    lijinke666 評論0 收藏0
  • dubbo源碼解析四十六)消費端發送請求過程

    摘要:可以參考源碼解析二十四遠程調用協議的八。十六的該類也是用了適配器模式,該類主要的作用就是增加了心跳功能,可以參考源碼解析十遠程通信層的四。二十的可以參考源碼解析十七遠程通信的一。 2.7大揭秘——消費端發送請求過程 目標:從源碼的角度分析一個服務方法調用經歷怎么樣的磨難以后到達服務端。 前言 前一篇文章講到的是引用服務的過程,引用服務無非就是創建出一個代理。供消費者調用服務的相關方法。...

    fish 評論0 收藏0
  • dubbo源碼解析四十七)服務端處理請求過程

    摘要:而存在的意義就是保證請求或響應對象可在線程池中被解碼,解碼完成后,就會分發到的。 2.7大揭秘——服務端處理請求過程 目標:從源碼的角度分析服務端接收到請求后的一系列操作,最終把客戶端需要的值返回。 前言 上一篇講到了消費端發送請求的過程,該篇就要將服務端處理請求的過程。也就是當服務端收到請求數據包后的一系列處理以及如何返回最終結果。我們也知道消費端在發送請求的時候已經做了編碼,所以我...

    yzzz 評論0 收藏0
  • dubbo源碼解析四十三)2.7新特性

    摘要:大揭秘目標了解的新特性,以及版本升級的引導。四元數據改造我們知道以前的版本只有注冊中心,注冊中心的有數十個的鍵值對,包含了一個服務所有的元數據。 DUBBO——2.7大揭秘 目標:了解2.7的新特性,以及版本升級的引導。 前言 我們知道Dubbo在2011年開源,停止更新了一段時間。在2017 年 9 月 7 日,Dubbo 悄悄的在 GitHub 發布了 2.5.4 版本。隨后,版本...

    qqlcbb 評論0 收藏0
  • dubbo源碼解析(二十五)遠程調用——hessian協議

    摘要:客戶端對象字節輸出流請求對象響應對象增加協議頭發送請求獲得請求后的狀態碼三該類實現了接口,是創建的工廠類。該類的實現跟類類似,但是是標準的接口調用會采用的工廠類,而是的協議調用。 遠程調用——hessian協議 目標:介紹遠程調用中跟hessian協議相關的設計和實現,介紹dubbo-rpc-hessian的源碼。 前言 本文講解多是dubbo集成的第二種協議,hessian協議,He...

    xzavier 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<