再談Motan服務的啓動

之前簡要地過了一遍Motan服務的啓動過程,感覺理解得還不夠清楚,所以接下來會就有疑惑的幾個點來做一次分析。
和之前一樣,以Motan服務的啓動爲依託,分析啓動過程中的幾個重要節點以及類,以此來窺探Motan服務端的設計架構和原則。

  • ExtensionLoader
  • URL
  • ConfigHandler
  • Exporter
  • Provider
  • Protocol
  • EndpointFactory

ExtensionLoader

Motan自己做了一套類加載機制實現,默認加載META-INF/services下的配置,支持加載以SPI方式配置的擴展實現。
配置文件 配合 Spi註解、SpiMeta註解(或者Activition註解)可以實現動態擴展的功能,比如:Registry、Protocol等等。

URL

URL是motan框架的核心對象,保存了很多配置信息,ip、端口、服務接口、協議等等。
分爲服務URL註冊URL

服務URL:motan://10.39.72.34:8002/com.weibo.motan.demo.service.MotanDemoService2?maxContentLength=1048576&module=motan-demo-rpc&nodeType=service&accessLog=false&minWorkerThread=20&protocol=motan&isDefault=true&application=myMotanDemo&maxWorkerThread=800&shareChannel=true&refreshTimestamp=1516604423329&id=com.weibo.api.motan.config.springsupport.ServiceConfigBean2&export=demoMotan:8002&maxServerConnection=80000&group=motan-demo-rpc&

註冊URL:
zookeeper://127.0.0.1:2181/com.weibo.api.motan.registry.RegistryService?group=default_rpc

Provider

可以理解爲真正的服務提供者,它持有一個服務serviceImpl對象,提供從DefaultRequest中提取調用信息,並且以反射的方式觸發客戶端的調用,返回DefaultResponse對象。

ConfigHandler

構建完URL之後,通過ExtensionLoader加載出本次export的ConfigHandler對象。默認爲SimpleConfigHandler

@Spi(scope = Scope.SINGLETON)
public interface ConfigHandler {
    <T> ClusterSupport<T> buildClusterSupport(Class<T> interfaceClass, List<URL> registryUrls);
    <T> T refer(Class<T> interfaceClass, List<Cluster<T>> cluster, String proxyType);
    <T> Exporter<T> export(Class<T> interfaceClass, T ref, List<URL> registryUrls);
    <T> void unexport(List<Exporter<T>> exporters, Collection<URL> registryUrls);
}

ConfigHandler是用來處理構建出來的URL的。從接口的定義可以得知它的功能:構建集羣的支持、創建到服務的引用、發佈服務(到註冊中心)、撤銷發佈服務。從架構的層面來看,它完成了配置和協議層的轉化。
具體分析下它的export方法:

@Override
    public <T> Exporter<T> export(Class<T> interfaceClass, T ref, List<URL> registryUrls) {
        // 將URLEncoder編碼過的註冊URL取出,解碼(註冊url的embed參數中,存儲着所要發佈的serviceURL字符串)
        String serviceStr = StringTools.urlDecode(registryUrls.get(0).getParameter(URLParamType.embed.getName()));
        //將service URL 字符串轉成對象
        URL serviceUrl = URL.valueOf(serviceStr);
        // export service
        // 利用protocol decorator來增加filter特性
        //根據serviceURL提取出協議名
        String protocolName = serviceUrl.getParameter(URLParamType.protocol.getName(), URLParamType.protocol.getValue());
         //根據協議名加載出最  基本的協議的實現對象(沒有經過Filter配置的)
        //此處的實現爲DefaultRpcProtocol
        Protocol orgProtocol = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(protocolName);
         //返回DefaultProvider對象
       Provider<T> provider = getProvider(orgProtocol, ref, serviceUrl, interfaceClass);
        Protocol protocol = new ProtocolFilterDecorator(orgProtocol);
        //發佈服務
        Exporter<T> exporter = protocol.export(provider, serviceUrl);
        // register service
        register(registryUrls, serviceUrl);
        return exporter;
    }

Exporter

查看Exporter<T> exporter = protocol.export(provider, serviceUrl); 的實現:

...

String protocolKey = MotanFrameworkUtil.getProtocolKey(url);
        synchronized (exporterMap) {
            //同一個Exporter只能在同一個protocol中發佈一次
            Exporter<T> exporter = (Exporter<T>) exporterMap.get(protocolKey);
            if (exporter != null) {
                throw new MotanFrameworkException(this.getClass().getSimpleName() + " export Error: service already exist, url=" + url,
                        MotanErrorMsgConstant.FRAMEWORK_INIT_ERROR);
            }
            exporter = createExporter(provider, url);
            exporter.init();
            exporterMap.put(protocolKey, exporter);
            LoggerUtil.info(this.getClass().getSimpleName() + " export Success: url=" + url);
            return exporter;
        }

exporter = createExporter(provider, url);
最終把provider和服務url提交到了NettyEndpointFactory中:

public Server createServer(URL url, MessageHandler messageHandler) {
        //爲MessageHandler增加心跳功能,這是個內置的服務
        messageHandler = getHeartbeatFactory(url).wrapMessageHandler(messageHandler);
        synchronized (ipPort2ServerShareChannel) {
            String ipPort = url.getServerPortStr();
            String protocolKey = MotanFrameworkUtil.getProtocolKey(url);
            boolean shareChannel =
                    url.getBooleanParameter(URLParamType.shareChannel.getName(), URLParamType.shareChannel.getBooleanValue());
            if (!shareChannel) { // 獨享一個端口
                LoggerUtil.info(this.getClass().getSimpleName() + " create no_share_channel server: url={}", url);
                // 如果端口已經被使用了,使用該server bind 會有異常
                return innerCreateServer(url, messageHandler);
            }
            LoggerUtil.info(this.getClass().getSimpleName() + " create share_channel server: url={}", url);
            Server server = ipPort2ServerShareChannel.get(ipPort);
            if (server != null) {
                // can't share service channel
                if (!MotanFrameworkUtil.checkIfCanShallServiceChannel(server.getUrl(), url)) {
                    throw new MotanFrameworkException(
                            "Service export Error: share channel but some config param is different, protocol or codec or serialize or maxContentLength or maxServerConnection or maxWorkerThread or heartbeatFactory, source="
                                    + server.getUrl() + " target=" + url, MotanErrorMsgConstant.FRAMEWORK_EXPORT_ERROR);
                }
                saveEndpoint2Urls(server2UrlsShareChannel, server, protocolKey);
                return server;
            }
            url = url.createCopy();
            url.setPath(""); // 共享server端口,由於有多個interfaces存在,所以把path設置爲空
            server = innerCreateServer(url, messageHandler);
            ipPort2ServerShareChannel.put(ipPort, server);
            saveEndpoint2Urls(server2UrlsShareChannel, server, protocolKey);
            return server;
        }
    }

只是在這之前,把provider做了再次封裝,變成了MessageHandler對象(ProviderProtectedMessageRouter)

可以看到,它關聯上了provider,並且提供了一些其他的功能,比如說根據服務地址查找provider。
並且EndpointFactory維護了一個

public void register(URL url) {
        if (url == null) {
            LoggerUtil.warn("[{}] register with malformed param, url is null", registryClassName);
            return;
        }
        LoggerUtil.info("[{}] Url ({}) will register to Registry [{}]", registryClassName, url, registryUrl.getIdentity());
        //在ZookeeperRegitry中有實現
        doRegister(removeUnnecessaryParmas(url.createCopy()));
        registeredServiceUrls.add(url);
        // available if heartbeat switcher already open
        if (MotanSwitcherUtil.isOpen(MotanConstants.REGISTRY_HEARTBEAT_SWITCHER)) {
            available(url);
        }
    }

註冊的代碼中有一點值得注意,會將url從ZK中刪除並添加到unavailableServer節點下,
然而,只有在設置MotanConstants.REGISTRY_HEARTBEAT_SWITCHER爲true的應用,他們的服務纔會被添加到
server節點下,才能被客戶端發現到。

在ZookeeperRegitry中的註冊實現如下:

@Override
    protected void doRegister(URL url) {
        try {
            serverLock.lock();
            // 防止舊節點未正常註銷
            removeNode(url, ZkNodeType.AVAILABLE_SERVER);
            removeNode(url, ZkNodeType.UNAVAILABLE_SERVER);
            createNode(url, ZkNodeType.UNAVAILABLE_SERVER);
        } catch (Throwable e) {
            throw new MotanFrameworkException(String.format("Failed to register %s to zookeeper(%s), cause: %s", url, getUrl(), e.getMessage()), e);
        } finally {
            serverLock.unlock();
        }
    }

這樣服務發佈完成

-EOF-

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