服务治理中间件 Dubbo 原理解析(注册中心)读书笔记

服务注册与发现的中心接口及参数介绍

服务的提供者将服务发布到注册中心,服务的使用着到 注册中引用服务。
Dubbo 的注册中心提供了多种实现,其实现是基于 dubbo 的 spi 的扩展机制的,
使用着可以直接实现自己的注册中心。
@SPI("dubbo")
public interface RegistryFactory {
/**
* 连接注册中心.
* 连接注册中心需处理契约
* 1. 当设置check=false时表示不检查连接,否则在连接不上时抛出异常。
* 2. 支持URL上的username:password权限认证。
* 3. 支持backup=10.20.153.10备选注册中心集群地址。
* 4. 支持file=registry.cache本地磁盘文件缓存。
* 5. 支持timeout=1000请求超时设置。
* 6. 支持session=60000会话超时或过期设置。
* @param url 注册中心地址,不允许为空
* @return 注册中心引用,总不返回空
*
    /@Adaptive({"protocol"})
    Registry getRegistry(URL url);
}

RegistryFactory 用来创建注册中心, 默认的注册中心是 dubbo 协议,由
于阿里的注册中心并没有开源, dubbo 协议注册中心只提供了一个简单实现。
开源 dubbo 的注册中心推荐使用 zookeeper。这里我们主要去分析基于 dubbo
和 zookeeper 协议的注册中心实现及使用。
一。服务接口定义
public interface RegistryService {
    void register(URL url);
    void unregister(URL url);
    void subscribe(URL url, NotifyListener listener);
    void unsubscribe(URL url, NotifyListener listener);
    List<URL> lookup(URL url);
}
二。Register: 注册数据,比如:提供者地址,消费者地址,路由规则,覆盖规则等数据。
注册需处理契约
1. 当URL设置了check=false时,注册失败后不报错,在后台定时重试,否则抛出异常。
2. 当URL设置了dynamic=false参数,则需持久存储否则,当注册者出现断电等情况异常退出需自动删除。
3. 当URL设置了category=routers时,表示分类存储,缺省类别为providers,可按分类部分通知数据。
4. 当注册中心重启,网络抖动,不能丢失数据,包括断线自动删除数据。
5. 允许URI相同但参数不同的 URL 并存,不能覆盖。
三。Unregister:取消注册
四。Subscribe: 订阅符合条件的已注册数据,当有注册数据变更时自动推送
订阅需处理契
1. 当URL设置了check=false时,订阅失败后不报错,在后台定时重试。
2. 当URL设置了category=routers,只通知指定分类的数据,多个分
   类用逗号分隔,并允许星号通配,表示订阅所有分类数据。
3. 允许以interface,group,version,classifier作为条件查询,
   如: interface=com.alibaba.foo.BarService&version=1.0.0
4. 并且查询条件允许星号通配,订阅所有接口的所有分组的所有版本,
   或interface=*&group=*&version=*&classifier=*
5. 当注册中心重启,网络抖动,需自动恢复订阅请求
6. 允许URI相同但参数不同的URL并存,不能覆盖
7. 必须阻塞订阅过程,等第一次通知完后再返回。
五。Unsubscribe:取消订阅
Lookup: 查询符合条件的已注册数据,与订阅的推模式相对应,这里为拉模式,
只返回一次结果
二。 Dubbo 协议注册中心
基于 dubbo 协议开源只是给出了默认一个注册中心实现 SimpleRegistryService,
它只是一个简单实现,不支持集群,就是利用 Map<String/*ip:port*/,Map<String/*service*/ URL>
来存储服务地址, 具体不在啰嗦了,请读者翻看源代码,可作为自定义注册中的参考。
(一)注册中心启动
SimpleRegistryService 本身也是作为一个 dubbo 服务暴露。
    <dubbo:protocol port="9090" />
    <dubbo:service interface="com.alibaba.dubbo.registry.RegistryService"
        ref="registryService" registry="N/A" ondisconnect="disconnect"
        callbacks="1000">
        <dubbo:method name="subscribe"><dubbo:argument index="1"
            callback="true" /></dubbo:method>
        <dubbo:method name="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"/>
 <bean id="demoService" class="com.alibaba.dubbo.demo.provider.DemoServiceImpl" />
 <dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService" />
1. 指定了哪种的注册中心,是基于 dubbo 协议的,指定了注册中心的地址以及端口号
2. 发布 DemoService 服务,服务的实现为 DemoServiceImpl
   每个<dubbo:service/>在 spring 内部都会生成一个 ServiceBean 实例,
  ServiceBean 的实例化过程中调用 export 方法来暴露服务
  1. 通过 loadRegistries 获取注册中心 registryUrls
     用统一数据模型 URL 表示:
     protocol=registry 表示一个注册中心 url
     注册中心地址 127.0.0.1:9090
     调用注册中心的服务 RegistryService
     注册中心协议是 registry=dubbo
     。。。。。。
  2. 构建发布服务的 URL
     发布协议 protocol = dubbo
     服务提供者的地址为 192.168.0.102:20880
     发布的服务为 com.alibaba.dubbo.demo.DemoService
     。。。 。。。
  3. 遍历 registryUrls 向注册中心注册服务
     给每个 registryUrl 添加属性 key 为 export, value 为上面的发布服务
     url 得到 registryUrl
  4. 由发布的服务实例,服务接口以及 registryUrl 为参数,通过代理工厂
     proxyFactory 获取 Invoker 对象,Invoker 对象是 dubbo 的核心模型,
     其他对象都向它靠拢或者转换成它。
  5. 通过 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
     RegistryFactory.getRegistry(url) 通过工厂类创建注册中心,
     RegistryFactory 通过 dubbo 的 spi 机制获取对应的工厂类, 这里的是
     基于 dubbo 协议的注册中心,所以是 DubboRegistryFactory
3. 获取发布 url 就是 registryUrl 的 export 参数的值

4. DubboRegistry.register(registryProviderUrl)
   通过注册器向注册中心注册服务这里注意 registryProviderUrl 的并没有设置category属性在注
   册中心 UrlUtils.ismatch(conuumerUrl, providerUrl)比较的时候,providerUrl的category
   属性取默认值 providers,这点消费者订阅的时候会指定订阅的 url 的 category=providers,去判
   断有没有注册的提供者。
5. 构建订阅服务 overrideProviderUrl,我们是发布服务
6. 构建 OverrideListener 它实现与 NotifyLisener, 当注册中心的订阅
   的 url 发生变化时回调重新 export
7. registry.subscribe(overrideProviderUrl, OverrideListener), 注册器向注册中心订阅 
   overrideProviderUrl, 同时将 Override
Listener 暴露为回调服务,当注册中心的 overrideProviderUrl 数据发生变化时回调,
注册器 DubboRegistry 的 registry, subscribe, unRegistry,
unSubscribe 都类似, 是一个 dubbo 的远程服务调用
DubboRegistryFactory 创建注册中心过程
  1. 根据传入 registryUrl 重新构建
     移除 EXPORT_KEY REFER_KEY
     添加订阅回调参数
  2. 根据 url 注册服务接口构建注册目录对象 RegistryDircectory,实现了
     NotiyfLisener,这里 NotiyfLisener 实现主要是根据 urls 去 refer
     引用远程服务 RegistryService 得到对应的 Invoker,当 urls 变化时
     重新 refer;目录服务可以列出所有可以执行的 Invoker
  3. 利用cluster的 join 方法,将 Dirctory 的多个Invoker对象伪装成一
     个Invoker对象, 这里默认集群策略得到 FailoverClusterInvoker
  4. FailoverClusterInvoker 利用 ProxyFactory 获取到
     RegistryService 服务的代理对象
  5. 由 RegistryService 服务的代理对象和 FailoverClusterInvoker 构
     建 dubbo 协议的注册中心注册器 DubboRegistry
  6. RegistryDircectory 设置注册器 DubboRegistry,设置 dubbo 的协议
  7. 调用 RegistryDircectory 的 notify(urls)方法
    主要是根据 registryUrls,引用各个注册中心的RegistryService服务实现,将引用的服务按 
    key=menthodName/value=invoker 缓存起来,目录服务 Directory.list(Invocation) 
    会列出所调用方法的所有Invoker , 一个 Invoker 代表对一个注册中心的调用实体。
  8. 订阅注册中心服务, 服务的提供者调注册 中心的服务 RegistryService属于消费方,
     所以订阅服务的 url 的协议是 consumer订阅的目的在于在注册中心的数据发送变化的时候
     反向推送给订阅方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
它至少向每个注册中心遍历调用注册一遍
三: Zookeeper 协议注册中心
下面我们来看下开源 dubbo 推荐的业界成熟的 zookeeper 做为注册中心,
zookeeper 是 hadoop 的一个子项目是分布式系统的可靠协调者,他提供了配
置维护,名字服务,分布式同步等服务。

 

 

1.Dubbo 服务提供者配置
<dubbo:registry protocol=” zookeeper” address="127.0.0. 1:2181" />
<bean id="demoService" class="com.alibaba.dubbo.demo.provider.DemoServiceImpl" />
<dubbo:service interface="com.alibaba.dubbo.demo.DemoService"ref="demoService" />
除了配置注册中心的,其他都一样
2.Dubbo 服务消费者配置
<dubbo:registry protocol=” zookeeper” address="127.0.0. 1:2181" />
<dubbo:reference id="demoService"interface="com.alibaba.dubbo.demo.DemoService" />
除了配置注册中心的,其他都一样
3.客户端获取注册器
服务的提供者和消费者在 RegistryProtocol 利用注册中心暴露(export)和引用
(refer)服务的时候会根据配置利用 Dubbo 的 SPI 机制获取具体注册中心注册器
Registry registry = registryFactory.getRegistry(url);
这里的RegistryFactory是ZookeeperRegistryFactory看如下工厂代码
public class ZookeeperRegistryFactory extends AbstractRegistryFactory {
    public Registry createRegistry(URL url) {
        return new ZookeeperRegistry(url, zookeeperTransporter);
    }
}
这里创建zookeepr注册器ZookeeperRegistry
ZookeeperTransporter是操作zookeepr的客户端的工厂类,用来创建zookeeper
客户端,这里客户端并不是zookeeper源代码的自带的,而是采用第三方工具包,
主要来简化对zookeeper的操作,例如用zookeeper做注册中心需要对zookeeper
节点添加watcher做反向推送,但是每次回调后节点的watcher都会被删除,这些
客户会自动维护了这些watcher,在自动添加到节点上去。
接口定义:
@SPI("zkclient")
public interface ZookeeperTransporter {
    @Adaptive({Constants.CLIENT_KEY, Constants.TRANSPORTER_KEY})
    ZookeeperClient connect(URL url);
}
默认采用zkClient, dubbo源码集成两种zookeeper客户端,除了zkClient还有
一个是curator
4.ZookeeperRegistry 注册器的实现
    1. 构造器利用客户端创建了对 zookeeper 的连接,并且添加了自动回复连接的监听器。
    zkClient = zookeeperTransporter.connect(url);
    zkClient.addStateListener(new StateListener() {
        public void stateChanged(int state) {
        if (state == RECONNECTED)
            recover();
        }
    });
    2. 注册 url 就是利用客户端在服务器端创建 url 的节点,默认为临时节点,客
       户端与服务端断开,几点自动删除
       zkClient.create(toUrlPath(url),
       url.getParameter(Constants.DYNAMIC_KEY, true));
    3. 取消注册的 url,就是利用 zookeeper 客户端删除 url 节点
        zkClient.delete(toUrlPath(url));
    4. 订阅 url, 功能是服务消费端订阅服务提供方在 zookeeper 上注册地址,
       这个功能流程跟 DubboRegister 不一样, DubboRegister 是通过 Dubbo 注册
       中心实现 SimpleResgiter 在注册中心端,对 url 变换、过滤筛选然后将获取
       的 provierUrl(提供者 ulr)利用服务消费者暴露的服务回调在 refer。由于这里注册中心采用的是         
       zookeeper, zookeeper 不可能具有 dubbo 的业务
       逻辑,这里对订阅的逻辑处理都在消费服务端订阅的时候处理。
       1) 对传入 url 的 serviceInterface 是*代表订阅 url 目录下所有节点即所有
          服务,这个注册中心需要订阅所有
       2) 如果指定了订阅接口通过 toCategoriesPath(url)转换需要订阅的 url
          如传入 url
       3) 设配传入的回调接口 NotifyListener,转换成dubbo对zookeeper操作的ChildListener
       4) 以/dubbo/com.alibaba.dubbo.demo.DemoService/providers 为例创建
          节点 zkClient.create(path, false);
         但是一般情况下如果服务提供者已经提供服务,那么这个目录节点应该已
         经存在, Dubbo 在 Client 层屏蔽掉了创建异常。
       5) 以/dubbo/com.alibaba.dubbo.demo.DemoService/providers 为例给节
         点添加监听器,返回所有子目录
        List<String> children = zkClient.addChildListener(path,zkListener);
             if (children != null) {urls.addAll(toUrlsWithEmpty(url, path,hildren));}
        toUrlsWtihEmpty用来配置是不是需要订阅的url,是加入集合
       6) 主动根据得到服务提供者urls回调NotifyListener,引用服务提供者生成invoker可执行对象
5. 取消订阅url, 只是去掉url上的注册的监听器

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

icool_ali

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值