前言
上一篇文章提到ServiceConfig处理url信息后交给DubboProtocol进行服务的暴露,开启了netty服务器,随后可以看到ServiceConfig的代码就结束。那么是如何将服务写入到注册中心呢?
Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
Exporter<?> exporter = protocol.export(wrapperInvoker);
ServiceConfig有一段这样的代码,就是核心export代码,但是在这之前有一句:
registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString())
这段代码已经把url变成了这样的格式:
区别就是:没有修改之前的url是dubbo开头,按照spi逻辑会去调用DubboProtocol这个类,但是修改后是registry开头,实际上调用的类是RegistryProtocol
正文
zookeeper注册原理
Zookeeper是一个树形结构的注册中心,可以把它看作是目录结构。一共有四种类型节点:
- 持久节点:保证节点不丢失,重启后依然存在
- 持久顺序节点:持久基础上增加了顺序功能
- 临时节点:连接丢失和session超时后会被移除
- 临时顺序节点:同上解释
dubbo注册在zookeeper的树形结构和值如下:
----dubbo
---------service
----------------providers:dubbo://ip.端口/接口路径?key=..
----------------consumers:consumer://ip.端口/接口路径key=..
----------------routers:condition.....
----------------configurators:override.....
++注册中心主要作用是订阅发布,当一个新的服务发布的时候,其他节点可以自动跟新++
注册过程
rpc包下有三个实现类
其中有两个是Wrapper类,默认调用,根据前面SPI的介绍,wrapper类会按照顺序封装,进行链式调用
可以看到只要是registry的协议都会进入RegistryProtocol
进入RegistryProtocol的export方法
首先是doLocalExport方法
getCacheKey方法将URL解析成如下图所示,协议为dubbo,然后检查缓存
所以真正的本地暴露是进入的doLocalExport方法,然后再调用DubboProtocol,按照上一篇文章介绍的一样进行暴露并且开启netty服务器。
URL registryUrl = getRegistryUrl(originInvoker);
下一步获取regitryUrl,里面包含了RegistryService内部信息:
zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.2&export=dubbo%3A%2F%2F192.168.168.115%3A20880%2Fcom.alibaba.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddemo-provider%26bean.name%3Dcom.alibaba.dubbo.demo.DemoService%26bind.ip%3D192.168.168.115%26bind.port%3D20880%26dubbo%3D2.0.2%26generic%3Dfalse%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DsayHello%26pid%3D90732%26qos.port%3D22222%26side%3Dprovider%26timestamp%3D1608539235916&pid=90732&qos.port=22222×tamp=1608539235882
然后通过这个url去获取到Regitry实例
final Registry registry = getRegistry(originInvoker);
RegistryFactory也是个SPI接口,这里使用的是ZookeeperRegistryFactory
内部的ZookeeperTransporter也是个SPI方法,可以选择使用curator或者zkclient,默认curator,里面封装了zookeeper的操作方法。
获取到了Registry的实例,然后解析注册中心的url,就可以通过url连接上注册中心并且操作API了
zkclient的注册方法
创建了一个path,值就是url
然后订阅该url
新建了一个OverrideListener,然后调用subscribe方法
这是全量订阅服务,表示所有类别都订阅
这是只订阅某个类别:providers、consumers等
当有新的服务跟新的时候,会回掉notify方法,对url执行变跟
这是注册中心的接口图,AbstractRegistry内部缓存了注册中心可调用的服务列表,以空间换时间。而FailbackRegistry添加了重试功能,内部维护发起注册失败、取消注册失败、发起订阅失败、取消订阅失败的SET,包装了接口方法。而真实的实现方法则是使用模板方法的设计模式,均由子类实现,而父类只调用该方法。
例子
父类调用Do开头的方法,子类负责实现
最终的过程就是:
- ServiceConfig将URL封装成registry协议,交给RegistryProtocol
- RegistryProtocol调用doLocalExport,使用自定义的协议(默认Dubbo),进行本地调用
- 解析注册中心地址,根据配置的client获取操作类(默认curator)
- 将url注册上zookeeper(创建一个目录),然后订阅,如果注册中心有改变,调用notify方法通知本地
可以看到注册中心的代码是比较简单的,如果配置多个注册中心,在ServiceConfig会进行循环注册,注册的过程并不涉及到集群有关的处理。