Dubbo服務暴露

Dubbo 與 Spring 的結合

代碼在 dubbo-config 下的 dubbo-config-spring 模塊中

在這裏插入圖片描述

META-INF/dubbo.xsd

dubbo.xsd 定義了 dubbo 自定義標籤

META-INF/spring.schemas

spring.schemas 指定了 dubbo.xsd 文件的位置.

http\://code.alibabatech.com/schema/dubbo/dubbo.xsd=META-INF/dubbo.xsd

META-INF/spring.handlers

關聯 DubboNamespaceHandler 命名空間處理器和 dubbo.xsd 中的 targetNamespace,即 http://code.alibabatech.com/schema/dubbo

http\://code.alibabatech.com/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHand

DubboNamespaceHandler.init()

DubboNamespaceHandler.init(): 註冊各個自定義元素的解析器,所有元素都是交給 DubboBeanDefinitionParser 處理的,傳入不同的 Class 對象

public void init() { // 註冊BeanDefinitionParser用來解析dubbo定義的那些xml元素節點
    registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));// <dubbo:application name="dubbo-admin" />
    registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));// <dubbo:registry address="${dubbo.registry.address}" check="false" file="false" />
    registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));// <dubbo:reference id="registryService" interface="com.alibaba.dubbo.registry.RegistryService" check="false" />
    registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
    registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
    registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
    registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
    //  <dubbo:service  interface="com.alibaba.dubbo.demo.DemoService" group="g1" ref="demoService" filter="demo" deprecated="false" callbacks="1000" timeout="200000" accesslog="true"> 解析成 ServiceBean
    registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
    registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));

    registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser()); // 廢棄
}

DubboBeanDefinitionParser

DubboBeanDefinitionParser.parse()中
根據 beanClass 創建 RootBeanDefinition,設置爲非懶加載.
若未配置 id 屬性,則會根據 name 或 interface 屬性生成,並通過判斷 BeanRegistry 中是否存在該 id 的 bean,用 則增序列解決重複問題.
通過id註冊到BeanRegistry中,並設置 beanDefinition 的 id 屬性.
下面就是對 ServiceBean 等元素的各自的屬性配置的處理.

在Spring應用上下文啓動過程中,加載XML配置文件(比如提供者配置和消費者配置文件)爲 Resource 對象,並通過 XmlBeanDefinitionReader 調用BeanDefinitionParser 解析 XML 標籤爲 BeanDefinition,並註冊到 BeanDefinitionRegistry 中.

ServiceBean

ServiceBean 實現了 InitializingBean, DisposableBean, ApplicationContextAware, ApplicationListener<ContextRefreshedEvent>, BeanNameAware 這幾個接口.

  1. 實現的 ApplicationContextAware 接口的 setApplicationContext() 方法中,會:
    保存 applicationContext 引用.
    applicationContext 引用 保存到 SpringExtensionFactory 中.

  2. 實現的 InitializingBean 接口的 afterPropertiesSet() 方法中,會: 設置相關屬性.

- ServiceBean.afterPropertiesSet()
    - 從BeanFactory中獲取ProviderConfig,ProtocolConfig等,這些bean是之前在Spring bean解析時加載進來的.將這些配置對象賦值到ServiceBean中,當然也有可能在其父類中.
  1. 實現的 ApplicationListener<ContextRefreshedEvent> 接口的 onApplicationEvent(ContextRefreshedEvent event) 方法中,有服務暴露的相關邏輯.

服務本地暴露

- ServiceBean.onApplicationEvent()
    監聽spring容器初始化完成時觸發
    - ServiceConfig.doExport()根據配置選擇立即暴露或延遲暴露
        - 暴露服務doExportUrls()
            - loadRegistries(true)加載註冊中心 URL 數組
                - 將xml中配置的<dubbo:registry>解析成url對象(如registry://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.0&logger=jcl&pid=52008&qos.port=22222&registry=zookeeper&timestamp=1574666370052)
            - doExportUrlsFor1Protocol(protocolConfig, registryURLs)根據不同的協議把服務export到不同的註冊中心(如zk)上去
                - ServiceConfig.exportLocal()中:服務本地暴露
                    - 1.通過傳入的協議url構造本地injvm協議的url
                    - 2.通過proxyFactory.getInvoker()獲取Invoker
                        - ProxyFactory$Adaptive.getInvoker()方法
                        - 1.ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension("javassist")獲取StubProxyFactoryWrapper 對象
                        - 2.extension.getInvoker(arg0, arg1, arg2)通過ProxyFactory獲取Invoker對象
                            - StubProxyFactoryWrapper.getInvoker()直接調用內部的JavassistProxyFactory.getInvoker()
                                - JavassistProxyFactory.getInvoker()方法中
                                - 1.根據proxy對象創建Wrapper
                                - 2.根據proxy, type, url創建AbstractProxyInvoker對象,它的doInvoke()方法會調用wrapper類的invokeMethod()方法
                    - 3.protocol.export()將Invoker轉爲Exporter
                        - Protocol$Adaptive.export()生成的代理類
                            - 1.通過ExtensionLoader獲取name爲injvm的Protocol拓展對象.吐出的是ProtocolListenerWrapper的實例,包裹了InjvmProtocol
                            - 2.extension.export(invoker)
                                - ProtocolFilterWrapper.export()中
                                    - 1.buildInvokerChain()建立帶有 Filter 過濾鏈的 Invoker,再暴露服務
                                    - 2.protocol.export()
                                        - ProtocolListenerWrapper.export()中
                                        - 1.通過InjvmProtocol.export()把invoker暴露爲exporter
                                            - InjvmProtocol.export()中
                                            - 1.創建InjvmExporter對象
                                        - 2.獲取ExporterListener拓展對象列表
                                        - 3.創建ListenerExporterWrapper對象作爲exporter返回
                

ServiceConfig.exportLocal()中:服務本地暴露

private void exportLocal(URL url) {
    if (!Constants.LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) { // 協議不以injvm開頭
        // 1.創建本地 injvm URL
        URL local = URL.valueOf(url.toFullString())
                .setProtocol(Constants.LOCAL_PROTOCOL) // injvm
                .setHost(LOCALHOST) // 本地
                .setPort(0); // 端口=0
        ServiceClassHolder.getInstance().pushServiceClass(getServiceClass(ref));
        // 2.使用 ProxyFactory 創建 Invoker 對象
        // 3.使用 Protocol 暴露 Invoker 對象,將 Invoker 轉成 exporter
        Exporter<?> exporter = protocol.export(proxyFactory.getInvoker(ref, (Class) interfaceClass, local));
        // 添加到 `exporters`
        exporters.add(exporter);
        logger.info("Export dubbo service " + interfaceClass.getName() + " to local registry");
    }
}

這個 proxyFactory 是 ProxyFactory$Adaptive 對象,會根據傳入的參數創建具體的 proxyFactory 對象.
proxyFactory.getInvoker(ref, (Class) interfaceClass, local)傳入的參數分別是 服務具體實現類對象ref,服務接口DemoService,本地暴露的URL對象

ProxyFactory$Adaptive.getInvoker()方法

public com.alibaba.dubbo.rpc.Invoker getInvoker(java.lang.Object arg0, java.lang.Class arg1, com.alibaba.dubbo.common.URL arg2) throws com.alibaba.dubbo.rpc.RpcException {// 獲取Invoker.參數 arg0 爲 ref 對象, arg1 爲 服務接口(如 DemoService), args 爲 服務暴露的 URL
    if (arg2 == null) throw new IllegalArgumentException("url == null");
    com.alibaba.dubbo.common.URL url = arg2;
    String extName = url.getParameter("proxy", "javassist");// 從url參數中獲取 ProxyFactory 拓展使用的方式,默認是javassist
    if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.ProxyFactory) name from url(" + url.toString() + ") use keys([proxy])");
    com.alibaba.dubbo.rpc.ProxyFactory extension = (com.alibaba.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension(extName); // 獲取 ProxyFactory 接口 的拓展實現(默認使用的實現類爲 JavassistProxyFactory)
    return extension.getInvoker(arg0, arg1, arg2);// extension爲StubProxyFactoryWrapper實例,包裹了JavassistProxyFactory對象
}

通過 ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension("javassist") 獲取的是 JavassistProxyFactory 對象,同時 ProxyFactory 有個 Wrapper 實現類 StubProxyFactoryWrapper,會被 SPI AOP 包裝到 JavassistProxyFactory 對象 外層,所以最後吐出的是 StubProxyFactoryWrapper 對象.

public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
    // 1.根據傳入的 proxy 對象的類信息創建對它的包裝對象 Wrapper
    final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
    return new AbstractProxyInvoker<T>(proxy, type, url) {// 2.返回Invoker對象實例,這個invoker對象invoke()方法可以根據傳入的invocation對象中包含的方法名,方法參數來調用proxy對象 返回調用結果
        @Override
        protected Object doInvoke(T proxy, String methodName,
                                    Class<?>[] parameterTypes,
                                    Object[] arguments) throws Throwable {
            return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
        }
    };
}

Invoker -> Exporter

ProtocolFilterWrapper.export()方法中

public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    // 註冊中心
    if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
        return protocol.export(invoker); // 向註冊中心發佈服務的時候並不會組裝 filter 調用鏈
    }
    // 建立帶有 Filter 過濾鏈的 Invoker,再暴露服務
    return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));
}
/**
    * 創建帶 Filter 鏈的 Invoker 對象
    *
    * @param invoker Invoker 對象
    * @param key 獲取 URL 參數名
    * @param group 分組
    * @param <T> 泛型
    * @return Invoker 對象
    */
private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
    Invoker<T> last = invoker;
    // 獲得匹配激活的過濾器數組
    List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
    // 倒序循環 Filter,創建帶 Filter 鏈的 Invoker 對象
    if (!filters.isEmpty()) {
        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 Class<T> getInterface() {
                    return invoker.getInterface();
                }

                @Override
                public URL getUrl() {
                    return invoker.getUrl();
                }

                @Override
                public boolean isAvailable() {
                    return invoker.isAvailable();
                }

                @Override
                public Result invoke(Invocation invocation) throws RpcException {
                    return filter.invoke(next, invocation);
                }

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

                @Override
                public String toString() {
                    return invoker.toString();
                }
            };
        }
    }
    return last;
}

ProtocolListenerWrapper.export()

public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    // 註冊中心協議
    if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
        return protocol.export(invoker);
    }
    // 暴露服務,創建 Exporter 對象
    Exporter<T> exporter = protocol.export(invoker);
    // 獲得 ExporterListener 數組
    List<ExporterListener> listeners = Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class).getActivateExtension(invoker.getUrl(), Constants.EXPORTER_LISTENER_KEY));
    // 創建帶 ExporterListener 的 Exporter 對象
    return new ListenerExporterWrapper<T>(exporter, listeners);
}

InjvmProtocol.export()

public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    return new InjvmExporter<T>(invoker, invoker.getUrl().getServiceKey(), exporterMap);
}

使用本地暴露的原因

本地調用使用了 injvm 協議,是一個僞協議,它不開啓端口,不發起遠程調用,只在 JVM 內直接關聯,但執行 Dubbo 的 Filter 鏈.

服務遠程暴露

服務遠程暴露的總體流程:

1.將 ref 封裝爲 Invoker

2.將 Invoker 轉換爲 Exporter

3.啓動 Netty 服務端

4.註冊 provider 到 zk

5.zk 訂閱與通

6.返回 Exporter 實例

- 服務遠程暴露
- 使用 ProxyFactory 將 ref 創建成 Invoker 對象(這部分邏輯和本地暴露類似)
    - ProxyFactory$Adaptive.getInvoker()中
        - extension.getInvoker(arg0, arg1, arg2);
            - StubProxyFactoryWrapper.getInvoker()直接調用內部的JavassistProxyFactory.getInvoker()
                - JavassistProxyFactory.getInvoker()方法中
                - 1.根據傳入的 proxy對象(如DemoServiceImpl)的類信息創建對它的包裝對象Wrapper
                - 2.根據proxy, type, url創建AbstractProxyInvoker對象,它的doInvoke()方法會調用wrapper類的invokeMethod()方法
- 首先將AbstractProxyInvoker實例包裝成DelegateProviderMetaDataInvoker實例
- 使用 Protocol 暴露 Invoker 對象爲 Exporter
    - Protocol$Adaptive.export()中
    - 1.此時從傳入的invoker.url.protocol獲取到的協議爲"registry",獲取到的Protocol拓展點實現類爲包裹了DubboProtocol 對象的 ProtocolFilterWrapper
    - 2.extension.export(invoker)
        - ProtocolFilterWrapper.export()這個類在Invoker基礎上添加filter鏈
            - 1.因爲傳入的協議時"registry",向註冊中心暴露服務
                - ProtocolListenerWrapper.export()中
                - 1.調用RegistryProtocol.export(),向註冊中心註冊
                    - RegistryProtocol.export()中
                        - 1.doLocalExport()本地啓動服務
                            - RegistryProtocol.doLocalExport()中
                            - 1.嘗試從bounds緩存中獲取exporter
                            - 2.若1中獲取不到則創建exporter並緩存
                            - 3.根據invoker創建InvokerDelegete委託類,提供 獲取 invoker 的方法
                            - 4.調用Protocol$Adaptive.export()方法
                                - Protocol$Adaptive.export()這次傳入的協議是"dubbo"
                                    - ProtocolFilterWrapper.export()中
                                    - 1.buildInvokerChain()構建filter鏈
                                    - 2.protocol.export()調用DubboProtocol.export()
                                        - DubboProtocol.export()中
                                        - 1.通過invoker創建DubboExporter
                                        - // 2.參見netty啓動流程
                            - 5.將4中返回的exporter包裝成ExporterChangeableWrapper
                        - 獲取註冊中心,如zk
                            - 向註冊中心註冊自己
                                - FailbackRegistry.register(url)

ProtocolFilterWrapper.export()中

    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
        // 註冊中心
        if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
            return protocol.export(invoker);// 向註冊中心發佈服務的時候並不會組裝 filter調用鏈
        }
        // 建立帶有 Filter 過濾鏈的 Invoker,再暴露服務
        return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));
    }
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
    // 暴露服務
    // export invoker
    final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker);

    // 獲得註冊中心 URL
    URL registryUrl = getRegistryUrl(originInvoker);

    // 獲得註冊中心對象(啓動zookeeper)
    // registry provider
    final Registry registry = getRegistry(originInvoker);

    // 獲得服務提供者 URL
    final URL registedProviderUrl = getRegistedProviderUrl(originInvoker);

    //to judge to delay publish whether or not.判斷是否延遲發佈
    boolean register = registedProviderUrl.getParameter("register", true);

    // 向註冊中心訂閱服務消費者
    ProviderConsumerRegTable.registerProvider(originInvoker, registryUrl, registedProviderUrl);

    // 向註冊中心註冊服務提供者(自己)
    if (register) {
        register(registryUrl, registedProviderUrl);
        ProviderConsumerRegTable.getProviderWrapper(originInvoker).setReg(true); // 標記向本地註冊表的註冊服務提供者,已經註冊
    }

    // 使用 OverrideListener 對象,訂閱配置規則
    // Subscribe the override data
    // FIXME When the provider subscribes, it will affect the scene : a certain JVM exposes the service and call the same service. Because the subscribed is cached key with the name of the service, it causes the subscription information to cover.
    // 創建訂閱配置規則的 URL
    final URL overrideSubscribeUrl = getSubscribedOverrideUrl(registedProviderUrl);
    // 創建 OverrideListener 對象,並添加到 `overrideListeners` 中
    final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
    overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
    // 向註冊中心,發起訂閱
    registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
    //Ensure that a new exporter instance is returned every time export
    return new DestroyableExporter<T>(exporter, originInvoker, overrideSubscribeUrl, registedProviderUrl);
}

Netty

ExchangeHandlerAdapter 定義了與客戶端連接成功/斷開連接/接受到客戶端消息/響應消息,以及創造Invocation的方法

public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
    return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
}

HeaderExchangeHandler: 基於消息頭(Header)的信息交換服務器實現類

DecodeHandler: 解碼處理器,處理接收到的消息

Netty 啓動流程

- netty啓動流程
- openServer(url)啓動通信服務器(如Netty)
    - DubboProtocol.openServer()中
        - DubboProtocol.createServer()中
        - 1.創建ExchangeHandlerAdapter實例,作爲請求處理器
        - 2.Exchangers.bind(url,requestHandler)
            - Exchangers.bind()中
            - 1.根據url獲取Exchanger,返回的是HeaderExchanger對象
                - HeaderExchanger.bind()
                    - HeaderExchanger.bind()方法中
                    - 1. return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))))會對傳入的ExchangeHandlerAdapter包裝兩層,然後使用Transporters.bind()創建NettyServer對象.
                        - Transporters.bind()中
                        - 1.返回Transporter的適配對象
                        - 2.調用Transporter$Adaptive.bind()
                            - netty4/NettyTransporter.bind()通過new NettyServer(url, listener)創建nettyServer對象
                                - NettyServer(url,handler)構造器中會通過ChannelHandlers會傳入的DecodeHandler進行包裝
                                    - ChannelHandler.wrapInternal()方法中
                                    - 1.獲取Dispatcher的適配對象
                                    - 2.調用Dispatcher.dispatch(handler, url)
                                        - AllDispatcher.dispatch()中new AllChannelHandler(handler, url)
                                            - AllChannelHandler調用父類構造方法WrappedChannelHandler
                                                - WrappedChannelHandler()構造方法中
                                                - 1.創建線程池
                                    - 3.把2中的返回結果用HeartbeatHandler和MultiMessageHandler再包裝
                                - NettyServer.doOpen()
                - 1.創建
            - 2.exchanger.bind()
    - ExchangeServer.createServer() 創建ExchangeServer

Zookeeper

public ZookeeperRegistry(URL url, ZookeeperTransporter zookeeperTransporter) {
    super(url);
    if (url.isAnyHost()) {
        throw new IllegalStateException("registry address == null");
    }
    // 獲得 Zookeeper 根節點
    String group = url.getParameter(Constants.GROUP_KEY, DEFAULT_ROOT); // `url.parameters.group` 參數值
    if (!group.startsWith(Constants.PATH_SEPARATOR)) {
        group = Constants.PATH_SEPARATOR + group;
    }
    this.root = group;
    // 創建 Zookeeper Client
    zkClient = zookeeperTransporter.connect(url);
    // 添加 StateListener 對象.該監聽器,在重連時,調用恢復方法.
    zkClient.addStateListener(new StateListener() {
        public void stateChanged(int state) {
            if (state == RECONNECTED) {
                try {
                    recover();
                } catch (Exception e) {
                    logger.error(e.getMessage(), e);
                }
            }
        }
    });
}

AbstractRegistry:

  • 1.通用的註冊,訂閱,查詢,通知等方法
  • 2.讀取和持久化註冊數據到文件,以 properties 格式存儲

FailbackRegistry:
主要用來做失敗重試操作(包括: 註冊失敗,反註冊失敗,訂閱失敗,反訂閱失敗,通知失敗的重試);也提供了供 ZookeeperRegistry 使用的 zk 重連後的恢復工作的方法.

ZookeeperRegistry:
創建 zk 客戶端,啓動會話;並且調用 FailbackRegistry 實現 zk 重連後的恢復工作.

註冊到 zk 流程

- 註冊到zk流程
- RegistryProtocol.export()
- 1.獲得註冊中心 URL
    - 就是把url改爲zookeeper://協議開頭
- 2.獲得註冊中心對象(啓動zookeeper),通過ZookeeperRegistryFactory實例創建並返回ZookeeperRegistry對象
    - AbstractRegistryFactory.getRegistry(url)中
    - 1.加鎖 從緩存中獲得 Registry 對象
    - 2.若獲取不到,則createRegistry(url)創建 Registry 對象並緩存
        - ZookeeperRegistryFactory.createRegistry()中會new ZookeeperRegistry(url, zookeeperTransporter)創建ZookeeperRegistry實例
- 3.獲得服務提供者 URL
- 4.向註冊中心註冊服務提供者
    - RegistryProtocol.register(registedProviderUrl)
    - 1.從registryFactory中獲取registry
    - 2.registry.register()
        - ZookeeperRegistry.doRegister()
        - 1.通過url得到zk路徑,創建臨時節點
- 5.向註冊中心發起訂閱
    當前的provider訂閱了/dubbo/com.alibaba.dubbo.demo.DemoService/configurators,當其下的子節點發生變化(url)時,通知到provider,使得providerUrl發生變化,則提供者需要重新暴露
    - RegistryProtocol.export()
        - FailbackRegistry.subscribe()
            - ZookeeperRegistry.doSubscribe()
            - 創建持久節點/dubbo/com.alibaba.dubbo.demo.DemoService/configurators
- 6.返回Exporter
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章