注册中心作用

开篇首先想思考一个问题,没有注册中心 Dubbo 还能玩下去吗?

当然可以,只要知道服务提供者地址相关信息,消费者配置之后就可以调用。如果只有几个服务,这么玩当然没问题。但是生产服务动辄成千上百,如果每个服务都需要手写配置信息,想象一下是多么麻烦。

好吧,如果上面的问题都不是事的话,试想一下如果一个服务提供者在运行过程中宕机,消费者怎么办?消费者不知情,所以它还会不断把请求发往服务提供者,然后不断失败。这个时候唯一的办法就是修改服务地址信息,然后重启服务。

可以看到如果没有注册中心,分布式环境中服务查找发现将会非常麻烦,一切需要手工配置,无法完成自动化。所以这里就需要一个第三者,协调服务提供者与消费者之间关系,这就是注册中心。

注册中心主要作用如下:

  1. 动态加入,服务提供者通过注册中心动态的把自己暴露给消费者,无需消费者逐个更新配置文件。
  2. 动态发现服务,消费者可以动态发现新的服务,无需重启生效。
  3. 统一配置,避免本地配置导致每个服务配置不一致。
  4. 动态调整,注册中心支持参数动态调整,新参数自动更新到所有相关的服务节点。
  5. 统一管理,依靠注册中心数据,可以统一管理配置服务节点。

注册中心工作流程

注册中心工作流程总体比较简单,流程图大致如下:

主要工作流程可以分为如下几步:

  1. 服务提供者启动之后,会将服务注册到注册中心。
  2. 消费者启动之后主动订阅注册中心上提供者服务,从而获取到当前所有可用服务,同时留下一个回调函数。
  3. 若服务提供者新增或下线,注册中心将通过第二步的注册的回调函数通知消费者。
  4. dubbo-admin(服务治理中心)将会会订阅服务提供者以及消费者,从而可以在控制台管理所有服务提供者以及消费者。

Dubbo 之前版本主要可以使用 ZooKeeper,Redis 作为注册中心 ,而随着 Dubbo 版本不断更新,目前还支持 nacos,consul,etcd 等做为注册中心。

Dubbo 注册中心核心源码

ps: 以下源码基于 dubbo 2.7.3 版本

注册中心实现使用模板模式,源码位于 dubbo-registry 模块,类关系如下图:

最上层的 RegistryService 接口定义了核心方法,分别为注册,取消注册,订阅,取消订阅以及查询。

中间层抽象类主要实现通用逻辑,如:AbstractRegistry 实现缓存机制,FailbackRegistry 实现失败重试功能。

底层 ZookeeperRegistry等为具体实现类,实现与 ZooKeeper 等注册中心交互的逻辑。

接下去我们具体分析 AbstractRegistryFailbackRegistry 逻辑。

AbstractRegistry 缓存实现的原理

如果每次服务调用都需要调用注册中心实时查询可用服务列表,不但会让注册中心承受巨大的流量压力,还会产生额外的网络请求,导致系统性能下降。

其次注册中心需要非强依赖,其宕机不能影响正常的服务调用。

基于以上几点,注册中心模块在 AbstractRegistry 类中实现通用的缓存机制。这里的缓存可以分为两类,内存服务缓存以及磁盘文件缓存。

内存服务缓存

内存服务缓存很好理解也最容易实现,AbstractRegistry使用一个 ConcurrentMap保存相关信息。

 private final ConcurrentMap<URL, Map<String, List<URL>>> notified = new ConcurrentHashMap<>();

这个集合中 key 为消费者的 URL,而 value 为一个 Map 集合。这个内层 Map 集合使用服务目录作为 key,分别为 providers,routers,configurators,consumers 四类,value 则是对应服务列表集合。

磁盘文件缓存

由于服务重启就会导致内存缓存消失,所以额外增加磁盘文件缓存。

文件缓存默认位置位于 ${user.home}/.dubbo/文件夹,文件名为dubbo-registry-${application.name}-${register_address}.cache。可以设置 dubbo.registry.file 配置信息从而修改默认配置,实现源码如下:

String filename = url.getParameter(Constants.FILE_KEY, System.getProperty("user.home") + "/.dubbo/dubbo-registry-" + url.getParameter(Constants.APPLICATION_KEY) + "-" + url.getAddress() + ".cache");

ps: ${application.name} 取自 dubbo.application.name 信息,${register_address} 取值注册中心地址信息。缓存文件完整名称为:C:\Users\xxx/.dubbo/dubbo-registry-dubbo-auto-configure-consumer-sample-127.0.0.1:2181.cache

缓存文件内容使用 properties 配置文件格式,即 key=value 格式。key为服务接口名称,value 为服务列表,由于服务可能存在多个,将会使用空格分隔。

缓存文件的加载

dubbo 程序初始化的时候,AbstractRegistry 构造函数将会从本地磁盘文件中将数据读取到 Properties 对象实例中,后续都将会先写入 Properties,最后再将里面信息再写入文件。

缓存初始化的源码为下图。

缓存文件的保存与更新

缓存文件将会通过 AbstractRegistry#notify 方法保存或更新。客户端第一次订阅服务获取的全量数据,或者后续回调中获取到新数据,都将会调用 AbstractRegistry#notify 方法,用来更新内存缓存以及文件缓存。

notify 方法源码如下图:

在保存文件缓存方法中,首先把根据 URL 取出的数据,拼接成字符串,然后写入上面提到过的 properties 对象中,最后输出到文件中。

这里可以选择两种保存方式,同步或异步。由于 notify 可能被多次调用,为了提高系统能,系统默认使用异步方式保存。

saveProperties 方法源码如下:

doSaveProperties 方法最终将会将信息写入缓存。考虑到保存方法可能会被多个线程同时调用,这里使用 CAS 方法,首先比较版本大小,若小于,代表有新线程正在写入信息,本次更新直接丢弃。

其次考虑到多个 dubbo 应用可能共用一份缓存文件,所以这里使用文件排他锁当做分布式锁,防止多个应用并发操作同一份文件。

一旦文件写入异常或者获取锁失败,保存操作将会不断重试,直到超过最大次数。

ps: dubbo 2.7.2 之前重试没有设置最大次数,如果文件没有权限保存,保存将会一直失败,异步线程将会陷入死循环。

doSaveProperties 方法源码如下:

FailbackRegistry 重试机制

FailbackRegistry 继承 AbstractRegistry,实现了 registersubscribe等通用法,并增加 doRegisterdoSubscribe 等模板方法,交由子类实现。

如果 doRegister 等模板方法发生异常,会将失败任务放入集合,然后定时再次调用模板方法。

FailbackRegistry 失败重试集合分别为:

subscribe 方法为例,这里将会调用这些 doSubscribe 的模板方法。如果发生异常将会读取缓存文件中内容,然后加载服务。最后新建异步定时任务加入重试集合中,然后由定时器去重试这些任务。

FailbackRegistry#subscribe 方法源码:

addFailedSubscribed 中将会新建定时任务,然后交由定时器执行。定时任务默认最大重试次数为 3 次,调用时间间隔默认为 5 s。

addFailedSubscribed 源码如下:

其他失败重试任务都比较类似,全都继承自 AbstractRetryTask 父类,类关系如下图。

总结

本文主要讲述注册中心作用,工作流程,通用缓存机制以及失败重试机制。从中可以学到模板模式,以及多线程并发技巧。

这里没有涉及到具体注册中心实现,由于目前最主要使用 ZooKeeper 作为注册中心,所以下篇将会聊聊 ZooKeeper 注册中心原理,敬请期待。

帮助书籍

『深入理解 Apache Dubbo 与实战』

如果有人问你 Dubbo 中注册中心工作原理,就把这篇文章给他的更多相关文章

  1. Dubbo多注册中心和Zookeeper服务的迁移

    一.Dubbo多注册中心 1. 应用场景 例如阿里有些服务来不及在青岛部署,只在杭州部署,而青岛的其它应用需要引用此服务,就可以将服务同时注册到两个注册中心. consumer.xml <?xm ...

  2. Dubbo配置注册中心设置application的name使用驼峰命名法存在的隐藏项目启动异常问题

    原创/朱季谦 首先,先提一个建议,在SpringBoot+Dubbo项目中,Dubbo配置注册中心设置的application命名name的值,最好使用xxx-xxx-xxx这样格式的,避免随便使用驼 ...

  3. 转:ListView中getView的工作原理

    ListView中getView的工作原理: [1]ListView asks adapter “give me a view” (getView) for each item of the list ...

  4. 本版本延续MVC中的统一验证机制~续的这篇文章,本篇主要是对验证基类的扩展和改善(转)

    本版本延续MVC中的统一验证机制~续的这篇文章,本篇主要是对验证基类的扩展和改善 namespace Web.Mvc.Extensions { #region 验证基类 /// <summary ...

  5. 【DUBBO】zookeeper在dubbo中作为注册中心的原理结构

    [一]原理图 [二]原理图解释 流程:1.服务提供者启动时向/dubbo/com.foo.BarService/providers目录下写入URL2.服务消费者启动时订阅/dubbo/com.foo. ...

  6. Javaconfig形式配置Dubbo多注册中心

    多注册中心,一般用不到,但是某些情况下的确能解决不少问题,可以将某些dubbo服务注册到2套dubbo系统中,实现服务在2套系统间的共用. 网上的配置说明很多,但包括dubbo官方说明文档都是以xml ...

  7. Dubbo及注册中心原理

    本文首发于微信公众号[猿灯塔],转载引用请说明出处 今天是猿灯塔“365天原创计划”第4天. 今天呢!灯塔君跟大家讲: 一.Dubbo意义 网站应用的架构变化经历了一个从所有服务分布在一台服务器上(A ...

  8. Dubbo多注册中心

    一.创建提供者08-provider-registers (1) 创建工程 直接复制05-provider-group工程,并命名为08-provider-registers (2) 修改配置文件 二 ...

  9. Dubbo Multicast 注册中心即相关代码实现

    Dubbo 的 Multicast注册中心有下面特点: 不需要启动任何中心节点,只要广播地址一样,就可以互相发现 组播受网络结构限制,只适合小规模应用或开发阶段使用. 组播地址段: 224.0.0.0 ...

随机推荐

  1. Balking设计模式

    import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.ArrayLi ...

  2. TCP、UDP和HTTP简述整理

    http:是用于www浏览的一个协议.tcp:是机器之间建立连接用的到的一个协议. 1.TCP/IP是个协议组,可分为三个层次:网络层.传输层和应用层.在网络层有IP协议.ICMP协议.ARP协议.R ...

  3. UVA11988 【Broken Keyboard (a.k.a. Beiju Text)】:题解

    题目链接:https://www.luogu.org/problemnew/show/UVA11988 这题虽说是和链表有关的模拟,但其实并不是很需要啊,但蒟蒻用了(说的好听是练手,说的难听是太弱), ...

  4. RabbitMQ 从入门到精通(二)

    目录 1. 消息如何保障百分之百的投递成功? 1.1 方案一:消息落库,对消息状态进行打标 1.2 方案二:消息的延迟投递,做二次确认,回调检查 2. 幂等性 2.1 幂等性是什么? 2.2 消息端幂 ...

  5. Codeforces1144D(D题)Equalize Them All

    D. Equalize Them All You are given an array aa consisting of nn integers. You can perform the follow ...

  6. 微信小程序设计总结

    微信小程序是一种全新的连接用户与服务的方式,它可以在微信内被便捷地获取和传播,同时具有出色的使用体验. 小程序提供了一个简单.高效的应用开发框架和丰富的组件及API,帮助开发者在微信中开发具有原生 A ...

  7. static import和import的区别

    import static静态导入是JDK1.5中的新特性.一般我们导入一个类都用 import com.....ClassName;而静态导入是这样:import static com.....Cl ...

  8. JS中构造函数和普通函数有什么区别

    JS中构造函数有普通函数有什么区别? 1.一般规则 构造函数都应该以 一个大写字母开头,eg: function Person(){...} 而非构造函数则应该以一个小写字母开头,eg: functi ...

  9. itextpdf 解析带中文的html问题

    官网连接 官网上有很多DEMO,下面就说几个我碰到的问题! Question: 1. 中文不显示 或者是乱码(本打算用Apache pdfbox来实现业务,但是折腾了一个上午也没解决中午乱码问题,就找 ...

  10. 【pip】brew install pip 问题

    Mac 下用 brew install pip 命令安装 pip 时报错: Error: No available formula with the name "pip" Home ...