Dubbo——服务端启动过程分析

参考的https://blog.csdn.net/meilong_whpu/article/details/72157622

一. 触发服务注册过程

服务端的启动过程中,对服务暴露由ServiceBean类开始处理逻辑,该类继承了<dubbo:service>标签对应的配置类ServiceConfig,还实现了一系列Spring接口用于参与Spring容器的启动以及bean的创建过程中去。ServiceBean类实现了InitializingBean接口的afterPropertiesSet方法。

对于注解方式注册服务,首先在在Spring回调AnnotationBean类的postProcessAfterInitialization方法时,触发调用ServiceBean. afterPropertiesSet ()方法,若服务设置为非延迟注册,则直接调用在ServiceConfig.export()方法触发服务注册过程。

对于XML方式注册服务,在spring的容器ApplicationConetext的启动过程refesh过程中最后第二步会预先初始化单例bean,在bean的初始化过程中会设置beanName,设置容器的appliactionContext并回调afterPropertiesSet方法,若此服务为延迟注册,则跳过此过程;在最后一步finishRefresh会触发ContextRefreshedEvent实际,而ServiceBean实现了ApplicationListener接口的onApplicationEvent方法监听此事件,从而在此方法被触发时调用export方法注册服务

上述设置服务注册延迟的方法是配置<dubbo:service>标签的delay属性。

 

Dubbo——服务端启动过程分析

二 .暴露服务 

1.服务暴露由ServiceConfig.export()方法处理,在该方法中首先获取配置参数export和delay,参数export表示是否暴露此服务,参数delay表示是否延迟暴露;若export为false或者未配置,则不进行后续的暴露逻辑处理(doExport方法);若delay有值,则启动一个守护线程,在delay时间之后进行暴露业务逻辑处理(doExport方法)。

Dubbo——服务端启动过程分析

Dubbo——服务端启动过程分析

2 生成注册地址

      在AbstractInterfaceConfig.loadRegistries(boolean provider)方法中生成注册地址。

Dubbo——服务端启动过程分析

Dubbo——服务端启动过程分析

[registry://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=boot-provider&dubbo=2.5.3&pid=11356&registry=zookeeper&timestamp=1536838754097] 

 

3.获取所有注册协议,然后遍历所有的服务暴露协议protocol,调用ServiceConfig.doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs)方法把每个服务根据配置的服务协议protocol暴露处理并向所有注册协议中注册

根据协议构建暴露服务的统一数据模型URL,例如根据如下配置

dubbo://192.168.132.1:20880/com.xbz.service.DemoService?anyhost=true&application=boot-provider&default.service.filter=testFilter&default.timeout=100000&delay=5000&dubbo=2.5.3&interface=com.xbz.service.DemoService&methods=sayHello&pid=13336&side=provider&timestamp=1536902978397

4.根据标签属性scope的参数值判断是否暴露服务。若为none,则不暴露服务;若等于“remote”,则进行远程暴露;若等于“local”则进行本地暴露;若为其他则本地和远程均暴露服务。默认情况下是本地和远程均暴露。

Dubbo——服务端启动过程分析

4.1本地暴露

ServiceConfig.exportLocal(URL url)方法中对url进行本地暴露,首先将URL的协议更改为了“injvm”、IP更改为了本地端口,端口变更为0。主要有代理工厂创建Invoker代理、Protocol暴露Invoker从而生成Exporter两个处理逻辑。

Dubbo——服务端启动过程分析

 此处用到了简单工厂模式,根据扩展点加载机制,选择的是StubProxyFactoryWrapper

Dubbo——服务端启动过程分析

Dubbo——服务端启动过程分析 在JavassistProxyFactory.getInvoker方法中创建了AbstractProxyInvoker类的子类对象,并重写了doInvoke方法,在该方法中通过反射的方式调用具体的实现类也就是说在消费端发送的请求会最终到此类的该方法中进行具体业务层处理逻辑的调用

 

Dubbo——服务端启动过程分析

对于本地暴露,protocol参数值为“injvm”,故选择InjvmProtocol类的export()方法进行服务暴露工作。

Dubbo——服务端启动过程分析

Dubbo——服务端启动过程分析

 Protocol类被ProtocolFilterWrapper、ProtocolListenerWrapper封装。在这两个类中对协议为“registry”的Invoker不做任何处理,对其他协议则进行过滤器链和监听器链的创建

Dubbo——服务端启动过程分析

 Dubbo——服务端启动过程分析

其中,ProtocolFilterWrapper类主要是对Invoker添加过滤器链,添加了如下过滤器:EchoFilter、ClassLoaderFilter、GenericFilter、ContextFilter、TraceFilter、TimeoutFilter、MonitorFilter、ExceptionFilter、ValidationFilter;具体的实现逻辑见《过滤器链的创建》。ProtocolListenerWrapper是对Exporter构建了监听器链,执行在服务暴露(exporter)的过程中监听器所提供的回调函数,Dubbo没有实现监听器,但提供了业务扩展的接口,业务可以实现ExporterListener接口或ExporterListenerAdapter抽象类来,并在xml的<dubbo:service>标签listener属性中配置监听器的类名即可。

 

最终返回的是包装过的exporter,将InjvmProtocol的export(Invoker)方法中创建的Exporter对象存入ServiceConfig对象的exporters属性中。至此本地暴露过程完成。

Dubbo——服务端启动过程分析

4.2远程暴露

将服务提供者的URL地址作为export参数的值放入注册地址URL中;

Dubbo——服务端启动过程分析

registry://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=boot-provider&dubbo=2.5.3&export=dubbo%3A%2F%2F192.168.132.1%3A20880%2Fcom.xbz.service.DemoService%3Fanyhost%3Dtrue%26application%3Dboot-provider%26default.service.filter%3DtestFilter%26default.timeout%3D100000%26dubbo%3D2.5.3%26interface%3Dcom.xbz.service.DemoService%26methods%3DsayHello%26pid%3D12200%26side%3Dprovider%26timestamp%3D1536908578040&pid=12200&registry=zookeeper&timestamp=1536908578017

调用代理工厂Protocol$Adpative根据注册地址URL中的协议选择具体的Protocol处理类,此处选择了RegistryProtocol.export()方法处理服务暴露的事情

Dubbo——服务端启动过程分析

以创建的Invoker代理作为参数调用doLocalExport方法启动服务端的服务容器,最终返回封装后的Exporter对象(ExporterChangeableWrapper),具体逻辑如下:

       1)用RegistryProtocol类的内部类InvokerDelegete封装注册Invoker代理对象,并将服务提供者的URL地址(注册地址URL中的export参数值 dubbo开头的)赋值给该内部类的URL变量;URL的协议为服务提供者与服务消费者间使用的协议,目前Dubbo版本中支持的协议有dubbo、mock、injvm、rmi、hessian、thrift、memcached、redis、rest。

Dubbo——服务端启动过程分析

       2)调用代理工厂Protocol$Adpative的export()方法处理服务暴露的事情,在该方法中根据服务提供者URL中的协议选择具体协议的Protocol实现类,并调用该实现类的export()方法处理服务暴露的事情。以DubboProtocol协议为例,在export方法之后返回DubboExporter对象,并被封装成ListenerExporterWrapper对象。

 

       3) 具体protocol暴露Invoker之后,创建了Exporter 对象,并将此对象存入AbstractProtocol.exporterMap中。其中key的组成方式:serviceGroup/serviceName:serviceVersion:port,serviceName是配置protocol的contextpath属性值+ dubbo:service的interface值,serviceGroup 为group属性值,serviceVersion为version属性值,若该属性值为空或者为“0.0.0”则在key中忽略此部分。从而可以看出在同一个协议下面是否为同一服务由上述属性决定。

Dubbo——服务端启动过程分析

Dubbo——服务端启动过程分析

   

开启Netty服务

Dubbo——服务端启动过程分析

 

根据注册地址URL获取Registry实例

 Dubbo——服务端启动过程分析Dubbo——服务端启动过程分析

然后通过得到的registry实例来进行注册 registry.register(registedProviderUrl);(registedProviderUrl以dubbo开头)

Dubbo——服务端启动过程分析

 这样就在zookeeper中创建了一个节点

Dubbo——服务端启动过程分析

dubbo 会基于这个 URL 生成一个新的URL,生成规则为:

"/dubbo/" + url里面的interface的值 + "/providers/" + URL.encode(url)

/dubbo/com.xbz.service.DemoService/providers/dubbo%3A%2F%2F192.168.132.1%3A20880%2Fcom.xbz.service.DemoService%3Fanyhost%3Dtrue%26application%3Dboot-provider%26default.service.filter%3DtestFilter%26default.timeout%3D100000%26dubbo%3D2.5.3%26interface%3Dcom.xbz.service.DemoService%26methods%3DsayHello%26pid%3D12200%26side%3Dprovider%26timestamp%3D1536908578040

然后在 Zookeeper 上面把 /dubbo/com.xbz.service.DemoService/providers/providers/ 创建成持久化节点,而后面部分的 URL 就会创建成临时节点。

把服务提供者信息创建成临时节点的好处就是如果当前服务挂掉,这个节点就会自动删除。这样失效服务就可以自动剔除。

 

Dubbo——服务端启动过程分析

4.3 配置监听器

     对服务提供者的configurators节点配置监听器。首先在服务提供者的节点树中创建configurators节点,然后配置监听器OverrideListener,若通过Dubbo管理系统为服务设置动态参数,则动态配置的参数放在configurators节点目录下,并通知服务端的OverrideListener监听器,根据动态参数重新生成服务提供者URL,若URL有变化则重新暴露服务。

 Dubbo——服务端启动过程分析

 overrideSubscribeUrl 如下值

provider://192.168.132.1:20880/com.xbz.service.DemoService?anyhost=true&application=boot-provider&category=configurators&check=false&default.service.filter=testFilter&default.timeout=100000&dubbo=2.5.3&interface=com.xbz.service.DemoService&methods=sayHello&pid=12200&side=provider&timestamp=1536908578040 

下面是zookeeper的doSubscribe方法

Dubbo——服务端启动过程分析

通过订阅 Zookeeper 上面的的节点信息变更, 可以通过 dubbo-admin来修改服务路由规则、权重等。

1、创建一个 NotifyListener 实例 OverrideListener, 当收到服务变更通知时触发。 
2、在 Zookeeper 注册中心创建持久化节点/dubbo/com.xbz.service.DemoService/configurators,用于接收 dubbo-admin这个客户端上对于集群的服务治理。

 

下面是与注册中心交互的相关逻辑,参考https://blog.csdn.net/lang_man_xing/article/details/51459685

 

l) 将初始化后的Server返回协议层 
i. 创建好的Server返回,此Server对象很重,缓存 
m) 协议层将暴露后的Exporter返回给注册中心协议层,最后返回给ServiceConfig进行缓存 
i. 协议层成功返回Exporter给注册中心协议层,标志着服务暴露完毕,接下来是与注册中心的操作,逻辑上看,注册中心负责服务的注册与发现,所有提供者向注册中心注册服务,并订阅重载配置,所有消费者向注册中心注册服务,并订阅服务,消费者通过注册中心自动发现所有服务提供者,且本地缓存提供者类表,注册中心宕机也不影响消费者调用;程序上看,注册中心也是普通的RPC服务,所有消费者、提供者与注册中心都是长连接。Dubbo目前注册中心服务有MulticastRegistry、ZookeeperRegistry等,关于注册中心的详细介绍待后期奉上。 
ii. RegistryProtocol.getRegistry(); 
iii. 得到具体注册中心服务且连接注册中心,此时提供者作为消费者引用注册中心核心服务RegistryService 
iv. Registry.register(url); 
v. 调用远端注册中心的register()方法进行服务注册,且若有消费者订阅此服务,则推送消息让消费者引用此服务,注册中心缓存了所有提供者注册的服务以供消费者发现。 
vi. Registry.subscribe(url,listener); 
vii. 提供者向注册中心订阅所有注册服务的覆盖配置,当注册中心有此服务的覆盖配置注册进来时,推送消息给提供者,让它重新暴露服务,这由管理页面完成。 
viii. 返回暴露后的Exporter给上层ServiceConfig进行缓存,便于后期撤销暴露。 
ix. 总结:Exporter的创建、Server的创建、服务的注册与订阅,这些逻辑都是分离的,它们是通过URL联系在一起的。一般情况下,同JVM同协议下的服务共享同一个Server,且消费端的引用这些服务的也可共享一个Client,从而实现多个服务共享同一个通道进行通信,且是基于长连接下,减少了通信的握手次数,高效率通信,另外,远程调用层与信息交换层及网络传输层是Dubbo的核心。