12. Dubbo原理解析-注册中心之基于dubbo协议的简单注册中心实现

基于dubbo协议开源只是给出了默认一个注册中心实现SimpleRegistryService, 它只是一个简单实现,不支持集群,就是利用Map<String/*ip:port*/, Map<String/*service*/, URL>来存储服务地址, 具体不在啰嗦了,请读者翻看源代码,可作为自定义注册中的参考。

注册中心启动

SimpleRegistryService本身也是作为一个dubbo服务暴露。

<dubbo:protocolport="9090" />

<dubbo:service interface="com.alibaba.dubbo.registry.RegistryService"ref="registryService" registry="N/A" ondisconnect="disconnect"callbacks="1000">

     dubbo:methodname="subscribe"<dubbo:argument index="1" callback="true"/></dubbo:method>

     dubbo:methodname="unsubscribe"<dubbo:argument index="1" callback="false"/></dubbo:method>

</dubbo:service>

<bean id="registryService"class="com.alibaba.dubbo.registry.simple.SimpleRegistryService" />

上面是暴露注册中心的dubbo服务配置,

定义了注册中心服务的端口号

发布RegistryService服务, registry属性是”N/A”代表不能获取注册中心,注册中心服务的发布也是一个普通的dubbo服务的发布,如果没有配置这个属性它也会寻找注册中心,去通过注册中心发布,因为自己本身就是注册中心,直接对外发布服务,外部通过ip:port直接使用。

服务发布定义了回调接口, 这里定义了subscribe的第二个入参类暴露的回调服务供注册中心回调,用来当注册的服务状态变更时反向推送到客户端。

 

Dubbo协议的注册中心的暴露以及调用过程过程跟普通的dubbo服务的其实是一样的,可能跟绝大多数服务的不同的是在SimpleRegistryService在被接收订阅请求subscribe的时候,同时会refer引用调用方暴露的NotifyListener服务,当有注册数据变更时自动推送

  

生产者发布服务

Dubbo协议向注册中心发布服务:当服务提供方,向dubbo协议的注册中心发布服务的时候,是如何获取,创建注册中心的,如何注册以及订阅服务的,下面我们来分析其流程。

看如下配置发布服务:

<dubbo:registry protocol=”dubbo” address="127.0.0.1:9090" />

<beanid="demoService" class="com.alibaba.dubbo.demo.provider.DemoServiceImpl"/>

<dubbo:serviceinterface="com.alibaba.dubbo.demo.DemoService" ref="demoService"/>

  1. 指定了哪种的注册中心,是基于dubbo协议的,指定了注册中心的地址以及端口号

  2. 发布DemoService服务,服务的实现为DemoServiceImpl

 

每个dubbo:service/在spring内部都会生成一个ServiceBean实例,ServiceBean的实例化过程中调用export方法来暴露服务

  1. 通过loadRegistries获取注册中心registryUrls

registry://127.0.0.1:9090/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.5.4-SNAPSHOT&owner=william&pid=7084&registry=dubbo&timestamp=1415711791506

用统一数据模型URL表示:

protocol=registry表示一个注册中心url

注册中心地址127.0.0.1:9090

调用注册中心的服务RegistryService

注册中心协议是registry=dubbo

。。。。。。

  1. 构建发布服务的URL

dubbo://192.168.0.102:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.5.4-SNAPSHOT&generic=false&interface=com.alibaba.dubbo.demo.DemoService&loadbalance=roundrobin&methods=sayHello&owner=william&pid=7084&side=provider&timestamp=1415712331601

发布协议protocol =dubbo

服务提供者的地址为192.168.0.102:20880

发布的服务为com.alibaba.dubbo.demo.DemoService

。。。。。。

  1. 遍历registryUrls向注册中心注册服务

给每个registryUrl添加属性key为export,value为上面的发布服务url得到如下registryUrl

registry://127.0.0.1:9098/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.5.4-SNAPSHOT& export=dubbo%3A%2F%2F192.168.0.102%3A20880%2Fcom.alibaba.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddemo-provider%26dubbo%3D2.5.4-SNAPSHOT%26generic%3Dfalse%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26loadbalance%3Droundrobin%26methods%3DsayHello%26owner%3Dwilliam%26pid%3D7084%26side%3Dprovider%26timestamp%3D1415712331601&owner=william&pid=7084&registry=dubbo&timestamp=1415711791506

  1. 由发布的服务实例,服务接口以及registryUrl为参数,通过代理工厂proxyFactory获取Invoker对象,Invoker对象是dubbo的核心模型,其他对象都向它靠拢或者转换成它。

  2. 通过Protocol对象暴露服务protocol.export(invoker)

通过DubboProtocol暴露服务的监听(不是此节内容)

通过RegistryProtocol将服务地址发布到注册中心,并订阅此服务

 

RegistryProtocol.export(Invoker)暴露服务

  1. 调DubboProtocol暴露服务的监听

  2. 获取注册中心getRegistry(Invoker)

URL转换, 由Invoker获取的url是registryURL它的协议属性用来选择何种的Protocol实例如RegistryProtocol, DubboProtocol或者RedisProtocol等等。 这里要通过URL去选择何种注册中心,所以根据registry=dubbo属性,重新设置url的协议属性得registryUrl

dubbo://127.0.0.1:9098/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.5.4-SNAPSHOT& export=dubbo%3A%2F%2F192.168.0.102%3A20880%2Fcom.alibaba.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddemo-provider%26dubbo%3D2.5.4-SNAPSHOT%26generic%3Dfalse%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26loadbalance%3Droundrobin%26methods%3DsayHello%26owner%3Dwilliam%26pid%3D5040%26side%3Dprovider%26timestamp%3D1415715706560&owner=william&pid=5040&timestamp=1415715706529

RegistryFactory.getRegistry(url) 通过工厂类创建注册中心,RegistryFactory通过dubbo的spi机制获取对应的工厂类, 这里的是基于dubbo协议的注册中心,所以是DubboRegistryFactory

  1. 获取发布url 就是registryUrl的export参数的值

registryProviderUrl=dubbo://10.33.37.7:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.5.4-SNAPSHOT&generic=false&interface=com.alibaba.dubbo.demo.DemoService&loadbalance=roundrobin&methods=sayHello&owner=william&pid=6976&side=provider&timestamp=1415846958825

  1. DubboRegistry.register(registryProviderUrl)

通过注册器向注册中心注册服务

这里注意registryProviderUrl的并没有设置category属性, 在注册中心UrlUtils.ismatch(conuumerUrl, providerUrl)比较的时候,providerUrl的category属性取默认值providers,

这点消费者订阅的时候会指定订阅的url的category=providers,去判断有没有注册的提供者。

  1. 构建订阅服务overrideProviderUrl,我们是发布服务

provider://10.33.37.7:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&category=configurators&check=false&dubbo=2.5.4-SNAPSHOT&generic=false&interface=com.alibaba.dubbo.demo.DemoService&loadbalance=roundrobin&methods=sayHello&owner=william&pid=6432&side=provider&timestamp=1415847417663

  1. 构建OverrideListener它实现与NotifyLisener,当注册中心的订阅的url发生变化时回调重新export

  2. registry.subscribe(overrideProviderUrl, OverrideListener), 注册器向注册中心订阅overrideProviderUrl,同时将Override Listener暴露为回调服务,当注册中心的overrideProviderUrl数据发生变化时回调,

注册器DubboRegistry的registry,subscribe, unRegistry, unSubscribe都类似, 是一个dubbo的远程服务调用

DubboRegistryFactory创建注册中心过程

  1. 根据传入registryUrl重新构建

移除EXPORT_KEY REFER_KEY

添加订阅回调参数

dubbo://127.0.0.1:9098/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&callbacks=10000&connect.timeout=10000&dubbo=2.5.4-SNAPSHOT& interface=com.alibaba.dubbo.registry.RegistryService&lazy=true&methods=register,subscribe,unregister,unsubscribe,lookup&owner=william&pid=8492&reconnect=false&sticky=true &subscribe.1.callback=true&timeout=10000&timestamp=1415783872554&unsubscribe.1.callback=false

  1. 根据url 注册服务接口构建注册目录对象RegistryDircectory,实现了NotiyfLisener,这里NotiyfLisener实现主要是根据urls去refer引用远程服务RegistryService得到对应的Invoker,当urls变化时重新refer;目录服务可以列出所有可以执行的Invoker

  2. 利用cluster的join方法,将Dirctory的多个Invoker对象伪装成一个Invoker对象, 这里默认集群策略得到FailoverClusterInvoker

  3. FailoverClusterInvoker利用ProxyFactory获取到RegistryService服务的代理对象

  4. 由RegistryService服务的代理对象和FailoverClusterInvoker构建dubbo协议的注册中心注册器DubboRegistry

  5. RegistryDircectory设置注册器DubboRegistry,设置dubbo的协议

  6. 调用 RegistryDircectory的notify(urls)方法

主要是根据registryUrls, 引用各个注册中心的RegistryService服务实现,将引用的服务按key=menthodName/value=invoker缓存起来, 目录服务Directory.list(Invocation)会列出所调用方法的所有Invoker , 一个Invoker代表对一个注册中心的调用实体。

  1. 订阅注册中心服务, 服务的提供者调注册中心的服务RegistryService属于消费方, 所以订阅服务的url的协议是consumer

consumer://192.168.0.102/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&callbacks=10000&connect.timeout=10000&dubbo=2.5.4-SNAPSHOT&interface=com.alibaba.dubbo.registry.RegistryService&lazy=true&methods=register,subscribe,unregister,unsubscribe,lookup&owner=william&pid=6960&reconnect=false&sticky=true &subscribe.1.callback=true&timeout=10000&timestamp=1415800789364& unsubscribe.1.callback=false

订阅的目的在于在注册中心的数据发送变化的时候反向推送给订阅方

directory.subscribe(url)最终调用注册中心的RegsryService远程服务, 它是一个普通的dubbo远程调用。要说跟绝大多数dubbo远程调用的区别:url的参数 subscribe.1.callback=true它的意思是RegistryService的subscribe方法的第二个参数NotifyListener暴露为回调服务; url的参数 unsubscribe.1.callback=false的意思是RegistryService的 unsubscribe方法的第二个参数NotifyListener暴露的回调服务销毁。

 

这里dubbo协议的注册中心调注册中心的服务采用的默认集群调用策略是FailOver,选择一台注册中心,只有当失败的时候才重试其他服务器,注册中心实现也比较简单不具备集群功能, 如果想要初步的集群功能可以选用BroadcastCluster它至少向每个注册中心遍历调用注册一遍

 

 

消费者引用服务

  < dubbo:registry  protocol=”dubbo”  address="127.0.0.1:9098"  />

< dubbo:reference  id="demoService" interface="com.alibaba.d ubbo.demo.DemoService" />

  1. 指定了哪种的注册中心,是基于dubbo协议的,指定了注册中心的地址以及端口号

  2. 引用远程DemoService服务

 

每个dubbo:reference/标签spring加载的时候都会生成一个Referenc eBean。

如上图ReferenceBean实现了spring的FactoryBean接口, 实现了此接口的Bean通过spring的BeanFactory.getBean(“beanName”)获取的对象不是配置的bean本身而是通过FactoryBean.getObject()方法返回的对象,此接口在spring内部被广泛使用,用来获取代理对象等等。这里getObjec t方法用来生成对远程服务调用的代理

  1. loadRegistries()获取配置的注册中心的registryUrls

  2. 遍历registryUrls集合,给registryUrl加上refer key就是要引用的远程服务

[ registry://127.0.0.1:9098/com.alibaba.dubbo.registry.RegistryService?application=demo-consumer&dubbo=2.0.0&pid=2484& refer=application%3Ddemo-consumer%26dubbo%3D2.0.0%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DsayHello%26pid%3D2484%26side%3Dconsumer%26timestamp%3D1415879965901&registry=dubbo&timestamp=1415879990670]

  1. 遍历registryUrls集合,使用Protocol.refer(interface,regist ryUrl)的到可执行对象invoker

  2. 如果注册中心有多个的话, 通过集群策略Cluser.join()将多个invoke r伪装成一个可执行invoker, 这里默认使用available策略

  3. 利用代理工厂生成代理对象proxyFactory.getProxy(invoker)

 

 

Protocol.refer过程流程

  1. 根据传入的registryUrl是用来选择RegistryProcol它的协议属性是registry, 下面要选择使用哪种注册中心所以要根据REGISTRY_KEY属性重新设置registrUrl

dubbo://127.0.0.1:9098/com.alibaba.dubbo.registry.RegistryService?application=demo-consumer&dubbo=2.0.0&pid=4524&refer=application%3Ddemo-consumer%26dubbo%3D2.0.0%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DsayHello%26pid%3D4524%26side%3Dconsumer%26timestamp%3D1415881461048&timestamp=1415881461113

  1. 根据registrUrl利用RegistryFactory获取注册器(过程跟暴露服务那边一样), 这里是dubbo协议得到的是注册器是DubboRegistry

引用并订阅注册中心服务,

  1. 构建引用服务的subscribeUrl

consumer://10.5.24.221/ com.alibaba.dubbo.demo.DemoService?application=demo-consumer& category=consumers&check=false&dubbo=2.0.0&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=8536&side=consumer&timestamp=1415945205031

并通过注册器向注册中心注册消费方, 主要这里的category是consume rs

  1. 构建目录服务RegistryDirectory,

构建订阅消费者订阅url,这里主要 category=providers去注册中心寻找注册的服务提供者

consumer://10.33.37.4/com.alibaba.dubbo.demo.DemoService?application=demo-consumer& category=providers,configurators,routers&dubbo=2.0.0&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=9692&side=consumer&timestamp=1415967547508

向注册中心订阅消费方,注册中心根据消费者传入的url找到匹配的服务提供者url (注意:这里服务提供者没有设置 category ,注册中心对于没有设置的默认取 providers 值) dubbo://10.33.37.4:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.5.4-SNAPSHOT&generic=false&interface=com.alibaba.dubbo.demo.DemoService&loadbalance=roundrobin&methods=sayHello&owner=william&pid=9828&side=provider&timestamp=1415968955329

然后注册中心回调服务消费者暴露的回调接口来对服务提供者的服务进行引用refer生成对应的可执行对象invoker。服务提供者与服务的消费建立连接,

  1. 通过Cluster合并directory中的invokers, 返回可执行对象invoker

  2. ProxyFactory.getProxy(invoker) 创建代理对象返回给业务方使用

  

这里dubbo协议的注册中心调注册中心的服务采用的默认集群调用策略是FailOver,选择一台注册中心,只有当失败的时候才重试其他服务器,注册中心实现也比较简单不具备集群功能, 如果想要初步的集群功能可以选用BroadcastCluster它至少向每个注册中心遍历调用注册一遍

代码交流 2021