Dubbo ServiceBean詳解

ServiceBean類圖

類繼承圖

在這裏插入圖片描述

成員變量和方法

在這裏插入圖片描述

服務暴露

服務暴露流程圖

在這裏插入圖片描述

我們看幾個核心的節點

ServiceConfig#doExport

protected synchronized void doExport() 

該方法爲同步方法,在初始化時同步阻塞。
方法開始時進行一些列的檢查,包含對provider,module,application,ref。

       if (ref instanceof GenericService) {
            interfaceClass = GenericService.class;
            if (StringUtils.isEmpty(generic)) {
                generic = Boolean.TRUE.toString();
            }
        } else {
            try {
                interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()
                        .getContextClassLoader());
            } catch (ClassNotFoundException e) {
                throw new IllegalStateException(e.getMessage(), e);
            }
            checkInterfaceAndMethods(interfaceClass, methods);
            checkRef();
            generic = Boolean.FALSE.toString();
        }

ref爲當前暴露對象的引用,首先檢查當前對象是否爲GenericService實例,否則會對對應的接口和方法進行檢查。

ServiceConfig#doExportUrls

    private void doExportUrls() {
        List<URL> registryURLs = loadRegistries(true);
        for (ProtocolConfig protocolConfig : protocols) {
            doExportUrlsFor1Protocol(protocolConfig, registryURLs);
        }
    }

Dubbo支持多註冊協議導出服務,也允許我們向多個註冊中心註冊服務,在上面的方法會加載對應的註冊中心,並在每個協議下導出服務。

ServiceConfig#doExportUrlsFor1Protocol

Dubbo導出服務默認爲dubbo協議

 String name = protocolConfig.getName();
        if (name == null || name.length() == 0) {
            name = "dubbo";
        }

然後獲取一些列基本參數
在這裏插入圖片描述

如果我們沒有配置scope爲remote,那麼dubbo總會執行一次injvm暴露

  // export to local if the config is not remote (export to remote only when config is remote)
            if (!Constants.SCOPE_REMOTE.toString().equalsIgnoreCase(scope)) {
                exportLocal(url);
            }

exportLocal

    private void exportLocal(URL url) {
        if (!Constants.LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
            URL local = URL.valueOf(url.toFullString())
                    .setProtocol(Constants.LOCAL_PROTOCOL)
                    .setHost(LOCALHOST)
                    .setPort(0);
            ServiceClassHolder.getInstance().pushServiceClass(getServiceClass(ref));
            Exporter<?> exporter = protocol.export(
                    proxyFactory.getInvoker(ref, (Class) interfaceClass, local));
            exporters.add(exporter);
            logger.info("Export dubbo service " + interfaceClass.getName() + " to local registry");
        }
    }

執行InjvmProtocol#export方法,執行injvm暴露

多註冊中心暴露

  if (registryURLs != null && registryURLs.size() > 0) {
                    for (URL registryURL : registryURLs) {

獲取對應invoker方法,並執行export

 Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
                        DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

                        Exporter<?> exporter = protocol.export(wrapperInvoker);

Invoker 創建過程

Invoker 是實體域,它是 Dubbo 的核心模型,其它模型都向它靠擾,或轉換成它,它代表一個可執行體,可向它發起 invoke 調用,它有可能是一個本地的實現,也可能是一個遠程的實現,也可能一個集羣實現。

    private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();

   Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
   DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

在這裏插入圖片描述

如上看到,dubbo的集中代理生成方式,Dubbo 默認的 ProxyFactory 實現類是 JavassistProxyFactory。

public class JavassistProxyFactory extends AbstractProxyFactory {

    @SuppressWarnings("unchecked")
    public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
        return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
    }

    public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
        // TODO Wrapper cannot handle this scenario correctly: the classname contains '$'
        final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
        return new AbstractProxyInvoker<T>(proxy, type, url) {
            @Override
            protected Object doInvoke(T proxy, String methodName,
                                      Class<?>[] parameterTypes,
                                      Object[] arguments) throws Throwable {
                return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
            }
        };
    }

}

核心在getInvoke時生成對應的getWrapper方法

    public static Wrapper getWrapper(Class<?> c) {
        while (ClassGenerator.isDynamicClass(c)) // can not wrapper on dynamic class.
            c = c.getSuperclass();

        if (c == Object.class)
            return OBJECT_WRAPPER;

        Wrapper ret = WRAPPER_MAP.get(c);
        if (ret == null) {
            ret = makeWrapper(c);
            WRAPPER_MAP.put(c, ret);
        }
        return ret;
    }

getWrapper調用makeWrapper生成對應的代理對象,代碼較多,暫不展示

RegistryProtocol#export

該方法主要負責完成:

  1. 調用 doLocalExport 導出服務
  2. 獲取註冊中心 URL
  3. 根據 URL 加載 Registry 實現類,比如 ZookeeperRegistry
  4. 取已註冊的服務提供者 URL
  5. 獲取 register 參數
  6. 向服務提供者與消費者註冊表中註冊服務提供者
  7. 根據 register 的值決定是否註冊服務
  8. 向註冊中心註冊服務
  9. 獲取訂閱 URL
  10. 創建監聽器
  11. 向註冊中心進行訂閱 override 數據
  12. 創建並返回 DestroyableExporter

真實export

在doLocalExport方法中,會調用具體的協議的暴露方法
RegistryProtocol#doLocalExport

  exporter = new ExporterChangeableWrapper<T>((Exporter<T>) protocol.export(invokerDelegete), originInvoker);

會根據對應的協議執行協議的export方法

DubboProtocol#export

    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
        URL url = invoker.getUrl();

        // export service.
        String key = serviceKey(url);
        DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
        exporterMap.put(key, exporter);

        //export an stub service for dispatching event
        Boolean isStubSupportEvent = url.getParameter(Constants.STUB_EVENT_KEY, Constants.DEFAULT_STUB_EVENT);
        Boolean isCallbackservice = url.getParameter(Constants.IS_CALLBACK_SERVICE, false);
        if (isStubSupportEvent && !isCallbackservice) {
            String stubServiceMethods = url.getParameter(Constants.STUB_EVENT_METHODS_KEY);
            if (stubServiceMethods == null || stubServiceMethods.length() == 0) {
                if (logger.isWarnEnabled()) {
                    logger.warn(new IllegalStateException("consumer [" + url.getParameter(Constants.INTERFACE_KEY) +
                            "], has set stubproxy support event ,but no stub methods founded."));
                }
            } else {
                stubServiceMethodsMap.put(url.getServiceKey(), stubServiceMethods);
            }
        }

        openServer(url);
        optimizeSerialization(url);
        return exporter;
    }
  1. 獲取註冊的url
  2. 獲取服務標識,理解成服務座標也行。由服務組名,服務名,服務版本號以及端口組成 serviceKey
  3. 將 <key, exporter> 鍵值對放入緩存中
  4. 啓動服務器
  5. 優化序列化

openServer

    private void openServer(URL url) {
        // find server.
        String key = url.getAddress();
        //client can export a service which's only for server to invoke
        boolean isServer = url.getParameter(Constants.IS_SERVER_KEY, true);
        if (isServer) {
            ExchangeServer server = serverMap.get(key);
            if (server == null) {
                serverMap.put(key, createServer(url));
            } else {
                // server supports reset, use together with override
                server.reset(url);
            }
        }
    }

最終創建NettyServer

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章