资讯专栏INFORMATION COLUMN

dubbo服务发布一之服务暴露

xialong / 2117人阅读

摘要:整体流程以调试来演示服务的发布流程。暴露远程服务假如服务没有配置了属性,或者配置了但是值不是,就会执行远程暴露。封装了一个服务的相关信息,是一个服务可执行体。是一个服务域,他是引用和暴露的主要入口,它负责的生命周期管理。

整体流程以调试 om.alibaba.dubbo.demo.provider.DemoProvider来演示dubbo服务的发布流程。

1、启动Spring容器

参照dubbo容器的启动, https://segmentfault.com/a/11... 文章描述

1.1、解析xml文件

dubbo的xml自定义标签,都是基于spring提供NamespaceHandlerSupport机制来完成的。
ServiceBean加载和初始化

   
    
public class DubboNamespaceHandler extends NamespaceHandlerSupport {

    static {
        Version.checkDuplicate(DubboNamespaceHandler.class);
    }

    public void init() {
        registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
        registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
        registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
        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));
        registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
        registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
        registerBeanDefinitionParser("annotation", new DubboBeanDefinitionParser(AnnotationBean.class, true));
    }

}

xml文件->ServiceBean.class 这就是spring的NamespaceHandlerSupport做的事情。

spring容器在初始化之后,会广播ContextRefreshedEvent事件,ServiceBean实现了ApplicationListener接口,在执行onApplicationEvent时,启动了export方法,开启 服务发布流程。

    public void onApplicationEvent(ApplicationEvent event) {
        if (ContextRefreshedEvent.class.getName().equals(event.getClass().getName())) {
            if (isDelay() && !isExported() && !isUnexported()) {
                if (logger.isInfoEnabled()) {
                    logger.info("The service ready on spring started. service: " + getInterface());
                }
                export();
            }
        }
    }

--export()
-----doExport()
-------doExportUrls()
---------loadRegistries()加载注册中心,可以有多个
---------doExportUrlsFor1Protocol()
-----------exportLocal(url) 本地暴露服务
-----------protocol.export(invoker) 远程暴露服务

2、本地暴露服务

假如服务没有配置了scope属性,或者配置了但是值不是”remote“,就会执行本地暴露。自同一个jvm内部,调用本jvm中存在的服务,就可以直接调用,而不需要走网络,减少响应时间。

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

第一步:设置URL中的参数,

protocal:injvm

host:127.0.0.1

port:0


url的协议已经从dubbo变成了injvm
第二步:将ref【interfaceClass的实现类】包装成一个Wrapper,并返回一个Invoker
JavassistProxyFactory#getInvoker

    public  Invoker getInvoker(T proxy, Class type, URL url) {
        // TODO Wrapper类不能正确处理带$的类名
        final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf("$") < 0 ? proxy.getClass() : type);
        return new AbstractProxyInvoker(proxy, type, url) {
            @Override
            protected Object doInvoke(T proxy, String methodName,
                                      Class[] parameterTypes,
                                      Object[] arguments) throws Throwable {
                return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
            }
        };
    }

第三步:根据injvm协议找到【这个是通过dubbo的Adaptive标签来动态决定的】InJvmProtocol,并执行他的export方法,装载Invoker。
第四步:将export放到该服务的exporters集合中。
本地暴露,不需要启动类似netty的服务器,也不需要注册zookeeper。

3、暴露远程服务

假如服务没有配置了scope属性,或者配置了但是值不是”local“,就会执行远程暴露。

if (!Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope)) {
                if (logger.isInfoEnabled()) {
                    logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
                }
                if (registryURLs != null && registryURLs.size() > 0
                        && url.getParameter("register", true)) {
                    for (URL registryURL : registryURLs) {
                        url = url.addParameterIfAbsent("dynamic", registryURL.getParameter("dynamic"));
                        URL monitorUrl = loadMonitor(registryURL);
                        if (monitorUrl != null) {
                            url = url.addParameterAndEncoded(Constants.MONITOR_KEY, monitorUrl.toFullString());
                        }
                        if (logger.isInfoEnabled()) {
                            logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL);
                        }
                        Invoker invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));

                        Exporter exporter = protocol.export(invoker);
                        exporters.add(exporter);
                    }
                } else {
                    Invoker invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);

                    Exporter exporter = protocol.export(invoker);
                    exporters.add(exporter);
                }

下面,我们来一步一步调试远程暴露都做了什么事情。

通过代理拿到Invoker,这一步就不细说了,和injvm的协议是一样的。

核心看着段代码:Exporter exporter = protocol.export(invoker);

protocol是一个Protocol$Adaptie类,是dubbo动态生成的对象。
如何获得动态生成的类的源码:将日志级别调测DEBUG,在控制台中拷贝出打印的源码,然后new相同的package、相同的java文件,就可以进去调试了。

看一下通过前面一些列操作之后,组装的Invoker对象

当前协议值为

我们再看一下export的值为

因为此时Invoker的协议为registry,所以会执行

package com.alibaba.dubbo.rpc;

import com.alibaba.dubbo.common.extension.ExtensionLoader;

public class Protocol$Adpative implements com.alibaba.dubbo.rpc.Protocol {
    public void destroy() {
        throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
    }

    public int getDefaultPort() {
        throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
    }

    public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException {
        if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
        if (arg0.getUrl() == null)
            throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");
        com.alibaba.dubbo.common.URL url = arg0.getUrl();
        String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
        if (extName == null)
            throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
        com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
        return extension.export(arg0);
    }

    public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException {
        if (arg1 == null) throw new IllegalArgumentException("url == null");
        com.alibaba.dubbo.common.URL url = arg1;
        String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
        if (extName == null)
            throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
        com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
        return extension.refer(arg0, arg1);
    }
}

从拿到的Extension的值,我们就能验证,dubbo的AOP原理。请戳链接https://segmentfault.com/a/11... 这个里面讲了为什么我们拿的是 RegistryProtocol,但是实际上确的拿到的是经过两个wrapper包装的对象的原因了?!

经过两次ProtocolListenerWrapper、ProtocolFilterWrapper的export方法之后,来到R
RegistryProtocol的export方法,这个是一个非常核心的方法。
]

doLocalExport方法里面,将从Invoker里面的拿到了export的值,重新构造了一个InvokerDelegete对象,这个时候又会执行Protocol$Adpative#export方法,这个时候Invoker的Url属性的协议已经是dubbo了。所以会拿到DubboProtocal,并执行export

DubboProtocol#export方法,发现有expoterMap属性的key是 com.alibaba.dubbo.demo.DemoService:20880 服务名:协议端口号。

3.1、启动netty服务器

从上图中,看到了openServer的方法了 <~.~> 。看一下入参URL如下,该值与上面一直图中的export的属性值一模一样。
【忽略ip、pid、timestamp字段,因为不是同一次调试的。】

属性serverMap存储了相应的server,ip:prot为key。因此,假如我们只配置一个协议端口,createServer只会执行一次。

执行Exchangers.bind(url, requestHandler) 就是为拿到 一个 ExchangeServer

]

在Exchangers里面, 执行getExchanger(url)拿到 HeaderExchanger执行bind方法

在HeaderExchanger里面,执行Transporters#bind也是为了拿到一个

在Transporters#bind里面,执行getTransporter()拿到默认的Transporter 即NettyTransporter。

在NettyTransporter启动一个NettyServer

netty服务器在父类的构造函数中被调用。

至此一个netty服务器就启动起来了。

大致完成的事情就是下图中红框框框起来的部分。

4、几个核心概念总结 4.1、ProxyFactory

实现了所有服务接口的的透明化代理。有两个方法,
getInvoker 服务端使用,将实现类封装成一个Invoker。
getProxy 客户端使用,创建接口的代理对象。

4.1、Ivoker

封装了一个服务的相关信息,是一个服务可执行体。

4.2、Invocation

是会话域,它持有调用过程中的变量,比如方法名,参数等。

4.3、Protocol

Protocol是一个服务域,他是Invoker引用和暴露的主要入口,它负责Invoker的生命周期管理。

4.4、Exporter

具体执行Invoker的生命周期

4.5、Exchange

封装请求响应模式,同步转异步

4.6、Transport

transport 网络传输层,抽象mina、netty的统一接口
..]

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/69175.html

相关文章

  • Dubbo服务暴露过程

    摘要:根据的值,进行服务暴露。如果配置为则不暴露,如果服务未配置成,则本地暴露如果未配置成,则暴露远程服务。提供者向注册中心订阅所有注册服务当注册中心有此服务的覆盖配置注册进来时,推送消息给提供者,重新暴露服务,这由管理页面完成。 概览 dubbo暴露服务有两种情况,一种是设置了延迟暴露(比如delay=5000),另外一种是没有设置延迟暴露或者延迟设置为-1(delay=-1): 设置了...

    bigdevil_s 评论0 收藏0
  • Dubbo服务提供者发布过程

    摘要:将标记为服务,使用对象来提供具体的服务。这整个过程算是该类的典型的执行过程。从上面得知服务发布的第一二个过程获取注册中心信息和协议信息。对于端来说,上述服务发布的第步中要解决的问题是根据指定协议向注册中心注册服务。 showImg(https://segmentfault.com/img/remote/1460000015274828?w=646&h=413); 上图是服务提供者暴露服...

    lifesimple 评论0 收藏0
  • Dubbo 一篇文章就够了:从入门到实战

    摘要:启动容器,加载,运行服务提供者。服务提供者在启动时,在注册中心发布注册自己提供的服务。注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。 一 为什么需要 dubbo 很多时候,其实我们使用这个技术的时候,可能都是因为项目需要,所以,我们就用了,但是,至于为什么我们需要用到这个技术,可能自身并不是很了解的,但是,其实了解技术的来由及背景知识,对...

    tomener 评论0 收藏0
  • Java9模块化学习笔记一之快速入门

    摘要:如果你想查看运行时模块的加载过程输出结果表示为模块,由于我限制了不再往下输出了,而我们模块又没有别的额外依赖,所以仅有这行输出。 jdk9模块快速入门 列出自带模块:java --list-modulesmac多版本jdk共存:http://adolphor.com/blog/2016...模块规则示意图:showImg(https://segmentfault.com/img/bVb...

    cjie 评论0 收藏0
  • dubbo服务发布二之zookeeper注册

    摘要:都是持久化节点,服务信息是零时节点,主要是为了监听服务提供方的是否断开连接,做出相应处理。订阅节点红框框里面主要做的时候就是创建服务对应的节点创建节点持久型节点并监听如下节点创建一个的监听器对服务提供者的节点配置监听器。 接着上一篇继续看。上一篇服务暴露已经讲完RegistryProtocol#doLocalExport的方法了。下面将服务是如何被写到zookeeper上的。showI...

    Hegel_Gu 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<