再谈Motan服务的启动

5 篇文章 0 订阅
3 篇文章 0 订阅

之前简要地过了一遍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-

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值