注册中心作用
- 动态加入,服务提供者通过注册中心动态的把自己暴露给消费者,消费者逐个更新配置文件
- 动态发现服务,小芬这可以动态发现新的服务,无需重启生效
- 统一配置,避免本地配置导致每个服务配置不一致
- 动态调整,注册中心支持参数动态调整,新参数自动更新到所有相关的服务节点
- 统一管理,依靠注册中心的数据,可以统一管理配置服务节点
主要工作流程
- 服务提供者启动之后,会将服务注册到注册中心
- 消费者启动后主动订阅注册中心上提供者服务,从而获得可用服务,同时留下一个回调函数
- 若服务提供者新增或者下线,注册中心将通过第二步的回调函数通知消费者
- 服务治理中心将会订阅服务提供者和消费者,从而可以在控制台管理多有的服务提供者以及消费者
Dubbo
- RegistryService 接口定义了核心方法,分别为注册,取消注册,订阅,取消订阅以及查询。
- 中间层抽象类主要实现通用逻辑,如:AbstractRegistry 实现缓存机制,FailbackRegistry 实现失败重试功能。
- 底层 ZookeeperRegistry等为具体实现类,实现与 ZooKeeper 等注册中心交互的逻辑。
AbstractRegistry 缓存实现的原理
内存服务缓存
private final ConcurrentMap<URL, Map<String, List<URL>>> notified = new ConcurrentHashMap<>();
key 为消费者的 URL,而 value 为一个 Map 集合。
内层 Map 集合使用服务目录作为 key,分别为 providers,routers,configurators,consumers 四类,value 则是对应服务列表集合
private final Set<URL> registered = new ConcurrentHashSet<URL>();
记录已经注册服务的URL集合,注册的URL不仅仅可以是服务提供者的,也可以是服务消费者的。
private final ConcurrentMap<URL, Set<NotifyListener>> subscribed = new ConcurrentHashMap<URL, Set<NotifyListener>>();
消费者url订阅的监听器集合
磁盘文件缓存
文件缓存默认位置位于 ${user.home}/.dubbo/文件夹,
文件名为dubbo-registry-${application.name}-${register_address}.cache
可以设置 dubbo.registry.file 配置信息从而修改默认配置
- AbstractRegistry 构造函数将会从本地磁盘文件中将数据读取到 Properties 对象实例中,
- 后续都将会先写入 Properties,
- 最后再将里面信息再写入文件。
客户端第一次订阅服务获取的全量数据,
后续回调获取到新数据,都将会调用 AbstractRegistry#notify 方法,用来更新内存缓存以及文件缓存。
doSaveProperties 方法最终将会将信息写入缓存,使用 CAS 方法
Eureka
- Eureka Server 启动成功,等待服务端注册。在启动过程中如果配置了集群,集群之间定时通过 Replicate 同步注册表,每个 Eureka Server 都存在独立完整的服务注册表信息
- Eureka Client 启动时根据配置的 Eureka Server 地址去注册中心注册服务
- Eureka Client 会每 30s 向 Eureka Server 发送一次心跳请求,证明客户端服务正常
- 当 Eureka Server 90s 内没有收到 Eureka Client 的心跳,注册中心则认为该节点失效,会注销该实例
- 单位时间内 Eureka Server 统计到有大量的 Eureka Client 没有上送心跳,则认为可能为网络异常,进入自我保护机制,不再剔除没有上送心跳的客户端
- 当 Eureka Client 心跳请求恢复正常之后,Eureka Server 自动退出自我保护模式
- Eureka Client 定时全量或者增量从注册中心获取服务注册表,并且将获取到的信息缓存到本地
- 服务调用时,Eureka Client 会先从本地缓存找寻调取的服务。如果获取不到,先从注册中心刷新注册表,再同步到本地缓存
- Eureka Client 获取到目标服务器信息,发起服务调用
- Eureka Client 程序关闭时向 Eureka Server 发送取消请求,Eureka Server 将实例从注册表中删除
Nacos
在NacosServiceRegistry.registry方法中,调用了Nacos Client SDK中的namingService.registerInstance完成服务的注册。
心跳机制:就是客户端通过schedule定时向服务端发送一个数据包 ,然后启动-个线程不断检测服务端的回应,如果在设定时间内没有收到服务端的回应,则认为服务器出现了故障。
- Nacos客户端通过Open API的形式发送服务注册请求
- Nacos服务端收到请求后,做以下三件事:
- 构建一个Service对象保存到ConcurrentHashMap集合中
- 使用定时任务对当前服务下的所有实例建立心跳检测机制
- 基于数据一致性协议服务数据进行同步
服务的动态更新,基本原理是:
- 客户端发起时间订阅后,在HostReactor中有一个UpdateTask线程,每10s发送一次Pull请求,获得服务端最新的地址列表
- 对于服务端,它和服务提供者的实例之间维持了心跳检测,一旦服务提供者出现异常,则会发送一个Push消息给Nacos客户端,也就是服务端消费者
- 服务消费者收到请求之后,使用HostReactor中提供的processServiceJSON解析消息,并更新本地服务地址列表
CAS算法:比较再交换
do{
备份旧数据;
基于旧数据构造新数据;
}while(!CAS( 内存地址,备份的旧数据,新数据 ))
例如:AtomicXXX
CAP定理
指的是在一个分布式系统中,Consistency(数据一致性)、 Availability(服务可用性)、Partition tolerance(分区容错性),三者不可兼得
当master节点因为网络故障与其他节点失去联系时,剩余节点会重新进行leader选举。选举期间注册服务瘫痪
Eureka的客户端在向某个Eureka注册或时如果发现连接失败,则会自动切换至其它节点。不过查到的信息可能不是最新的
-
数据一致性(Consistency)
也叫做数据原子性系统在执行某项操作后仍然处于一致的状态。在分布式系统中,更新操作执行成功后所有的用户都应该读到最新的值,这样的系统被认为是具有强一致性的。等同于所有节点访问同一份最新的数据副本。- 优点: 数据一致,没有数据错误可能。
- 缺点: 相对效率降低。
-
服务可用性(Availablity)
每一个操作总是能够在一定的时间内返回结果,这里需要注意的是"一定时间内"和"返回结果"。一定时间内指的是,在可以容忍的范围内返回结果,结果可以是成功或者是失败。 -
分区容错性(Partition-torlerance)
在网络分区的情况下,被分隔的节点仍能正常对外提供服务(分布式集群,数据被分布存储在不同的服务器上,无论什么情况,服务器都能正常被访问)