nacos源码解析——集群间数据同步


前言

之前分析过eurekaeureka是一个AP的注册中心,他的节点间数据同步是通过p2p来进行的,而nacos既是CP系统也是AP系统,临时节点的数据是存储在内存中,节点间的同步是通过P2P方式,永久节点则是通过raft来实现数据的一致性的,是属于CP的。


1、临时节点集群间同步

在服务注册的时候,如果是临时节点,我们会调用DistroConsistencyServiceImpl#put方法,之前讲解过写入map和写入内存队列,最后还会将变更数据同步到集群中的其他节点,这次来剖析一下同步流程,这里默认延迟时间是2s,然后/21s

在这里插入图片描述

1.1、DistroProtocol#sync

这里会遍历所有成员,然后抛去自己,然后调用syncToTarget方法

在这里插入图片描述

1.2、DistroProtocol#syncToTarget

这里他会把节点的地址也封装到DistorKey中,然后包装成一个Task,交给延迟任务引擎来处理

在这里插入图片描述

1.3、NacosDelayTaskExecuteEngine#addTask

这里就是判断如果任务存在就合并,不存在就添加进去,我们看下什么时候执行任务,肯定会从任务队列中获取任务然后执行。

在这里插入图片描述

1.4、NacosDelayTaskExecuteEngine构造方法

这里会创建一个延迟定时任务,每100ms执行一次,对应执行的任务在ProcessRunnable#run

在这里插入图片描述

1.5、ProcessRunnable#run

这里会从队列中获取任务,然后根据taskKey去获取对应的processor,调用调用process方法,这里匹配到的processorDistroHttpDelayTaskProcessor

在这里插入图片描述

1.6、DistroHttpDelayTaskProcessor#process

这里又封装成一个DistroHttpCombinedKeyExecuteTask,然后把这个task交给任务执行引擎
在这里插入图片描述

1.7、NacosExecuteTaskExecuteEngine#addTask

这里根据任务获取processor,这里没有处理这个任务的processor,所以会通过hash的方式获取一个TaskExecuteWorker,然后调用其processor方法

在这里插入图片描述
在这里插入图片描述

1.8、TaskExecuteWorker#process

这里会把任务put到一个队列中,我们看下什么地方会从队列中获取任务然后进行处理。

在这里插入图片描述

1.9 InnerWorker#run

TaskExecuteWorker的构造方法中会创建InnerWorker线程,并调用其start方法,在这个 方法中就不断从队列中取出任务然后执行任务,这里会调用taskrun方法,之前我们已经知道任务被封装成DistroHttpCombinedKeyExecuteTask,我们看下它的run方法

在这里插入图片描述

1.10 DistroHttpCombinedKeyExecuteTask#run

这里又封装了一个DistroHttpCombinedKeyDelayTask任务,然后这里设置延迟时间也是1s,最后把任务交给延迟任务执行引擎,这里会走merge任务的逻辑,在NacosDelayTaskExecuteEngine有个延迟任务,100ms执行一次,因为任务的延迟时间是1s,到第10次的时候会将task取出来,根据这个任务获取processor,获取不到默认是DistroDelayTaskProcessor,所以会调用DistroDelayTaskProcessor#process方法

在这里插入图片描述

1.11 DistroDelayTaskProcessor#process

这里又会封装成DistroSyncChangeTask,然后交给Worker线程执行,我们之前分析过,在worker处理的时候会调用taks#run方法,所以我们看下DistroSyncChangeTask#run
在这里插入图片描述

1.12、DistroSyncChangeTask#process

这个就是父类中的模版,做一些容错的处理,真正执行的时候会调用doExecute方法

在这里插入图片描述

1.13、DistroSyncChangeTask#doExecute

获取到真实要同步的数据,然后获取同步组件,调用syncData来发送同步数据

在这里插入图片描述

1.14、DistroHttpAgent#syncData

这里会调用NamingProxy#syncData来同步数据

在这里插入图片描述

1.15、NamingProxy#syncData

这里就是向集群其他节点发送同步数据。请求的接口是distro/datum,对应服务端的接口DistroController#onSyncDatum

在这里插入图片描述

1.16、DistroController#onSyncDatum

  1. 遍历map,判断key是不是临时数据的key,如果是的话,根据key解析出来namespaceserviceName
  2. 去注册表中看有没有这个服务,如果没有创建一个空的
  3. 调用DistroProtocol#onReceive处理数据

在这里插入图片描述

1.17、DistroController#onSyncDatum

  1. 首先通过resourceType获取一个DistroDataProcessor
  2. 调用DistroDataProcessor#processData方法

在这里插入图片描述

1.18、DistroConsistencyServiceImpl#onSyncDatum

  1. 首先对数据进行反序列化
  2. 调用onPut方法,这个方法之前分析过会将数据存入DataStore,并且将事件变更放入内存队列中。然后会有线程从队列中取任务处理,然后把数据更新到注册表中。

在这里插入图片描述

2、永久节点集群间同步

在之前的版本nacos同步采用的是自研的raft协议,2.0.0版本之后采用JRaft,这里我们还是基于之前的Raft讲解。

2.1、RaftCore#init(选举流程)

首先从本地文件加载数据到内存中,

  1. 然后注册了一个选举任务500ms执行一次。
  2. 注册了一个心跳任务500ms执行一次。

在这里插入图片描述

2.2、MasterElection#run(选举任务)

  1. 这里获取自己的节点信息,拿自己的leaderDueMs减去500,这个一开始是0~15000的随机数,只有减到负数才进行选举,如果减到负数了,则重置leaderDueMsheartbeatDueMs,然后就是向其他节点发送选票

在这里插入图片描述

2.3、MasterElection#sendVote

  1. 获取自己的peer,然后重置所有的选票信息,把自己的term++,然后投给自己一票,把自己的状态改成候选人状态。
  2. 向其他的节点发送拉票信息。节点收到其他节点的选票信息是在RaftCore#receivedVote方法中处理的。

在这里插入图片描述

2.4、RaftCore#receivedVote

这里就是对比term的大小,如果term小于对方则投票给对方,如果term大于对方,则把选票设置为自己,返回给拉票房。

在这里插入图片描述

2.5、RaftCore#decideLeader

拉票方收到对方的投票结果会调用decideLeader方法,这里就是判断所有节点收到的选票的数量,如果有节点到达半数以上,则选举那个节点为leader,如果本机为leader,则会发布leader选举事件

在这里插入图片描述

2.6、HeartBeat#run(心跳任务)

和选举任务差不多,也是对一个随机数做减法,减到0就可以继续往下走,发送心跳了。

在这里插入图片描述

2.6、HeartBeat#sendBeat(发送心跳)

这里只有leader节点才能发送心跳,这里就是压缩数据,向所有的follwer节点发送心跳。

在这里插入图片描述

2.7、RaftCore#receivedBeat(处理心跳)

Fellower节点接到心跳逻辑

  1. 首先比较term的大小,如果当前term比leader大则抛出异常,因为leader的版本是落后的。
  2. 重置本地的leaderDueMs和heartbeatDueMs,如果leader一直正常维持心跳,那么fellower是不会进行选举的。
  3. 会调用makeLeader方法,这个主要是看本地维护的leader和远程的leader是不是同一个,如果不是则进行更新。

在这里插入图片描述

2.8、RaftConsistencyServiceImpl#onPut

当服务注册到nacos中,他会调用consistencyService#put方法,把注册信息同步到集群其他节点,对于永久节点consistencyService的实现有两个,1.4版本之前是RaftConsistencyServiceImpl,之后是PersistentServiceProcessor。我们这次介绍RaftConsistencyServiceImpl
在这里插入图片描述

2.8、RaftCore#signalPublish

  1. 判断不是leader的话则会把请求转发给leader
  2. 如果是leader,则先进行加锁,然后调用onPublish方法进行本地存储。
  3. 通过给所有的fellower节点,这里用了这里用了CountDownLatch,阻塞等过半的节点成功就返回。最大等待5s,如果5s还没同步到过半节点,就抛出异常,释放锁资源。这里就会导致节点间的数据不一致了。所以它并不是强一致的,而是实现成最终一致的。

在这里插入图片描述
在这里插入图片描述

2.8、最终一致性的Raft协议

它实现最终一致性,就是leader在发送心跳的时候会将这个数据的keytimestamp(也相当于版本号)
带给follower节点,然后follower节点收到leader 发过来的心跳,会将本地的key ,timestamp
leader带过来的key,timestamp 进行比较,如果本地少了这个key ,或者是key对应的timestamp
低于leader的话,就会发送请求去leader那拉取不一致的数据

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值