JRaft源码剖析4-快照机制

1.为什么引入快照机制?

快照机制本质上也是一种对日志数据复制的优化手段。

两个问题:

  • 1)因为日志数据需要落盘存储,当日志数据量大到磁盘空间无法容纳时,除了扩容是否还有其它的优化手段?
  • 2)当一个新的节点加入 Raft 集群时需要重放集群之前接收到的所有指令以追赶上集群的数据状态,这一过程往往比较耗时和消费带宽,如何进行优化?

解决方法就是快照机制:

  • 快照机制通过定期为本地的数据状态生成对应的快照文件,并删除对应的日志文件,从而降低对于磁盘空间的容量消耗。
  • 当一个新的节点加入集群时,不同于从 Leader 节点复制集群在此之前的所有日志文件,基于快照机制该节点只需要从 Leader 节点下载安装最新的快照文件即可。

2.生成快照

如果在启动 JRaft 节点时指定了快照路径 snapshotUri,则表明业务希望启用快照机制。JRaft 节点会在初始化期间(即执行 Node#init 方法)启动快照计时器 snapshotTimer,用于周期性生成快照(默认周期为 1 小时)。该计时器的具体执行逻辑由 NodeImpl#handleSnapshotTimeout 方法实现,该方法会判断当前节点是否处于活跃状态,如果是则会异步调用 NodeImpl#doSnapshot 方法执行生成快照的操作。

NodeImpl#doSnapshot
-> SnapshotExecutorImpl#doSnapshot

  • 1)SnapshotExecutor 已被停止,返回
  • 2)正在安装快照,返回
  • 3)正在生成快照,不允许重复执行,返回
  • 4)状态机调度器最后应用的 LogEntry 已经被快照,说明没有新的数据可以被快照
  • 5)可以被快照的数据量小于阈值,暂不生成快照
  • 6)创建并初始化快照写入器,默认使用 LocalSnapshotWriter 实现类
  • 7)向状态机调度器发布一个 SNAPSHOT_SAVE 事件用于异步生成快照文件,同时会绑定一个 SaveSnapshotDone 回调以感知异步快照生成的状态。

FSMCallerImpl#doSnapshotSave处理事件

  • 1)构造快照元数据信息,封装当前被状态机应用的 LogEntry 的 logIndex 和 term 值,以及对应的集群节点配置信息
  • 2)记录快照元数据
  • 3)调用状态机 StateMachine#onSnapshotSave 方法生成快照

如果生成快照成功,需要调用 SnapshotWriter#addFile 方法将快照文件名和对应的元数据信息记录到快照元数据信息表中。这么做的目的除了能够让 JRaft 识别该快照文件,业务也可以在后续安装快照文件时读取到快照的元数据信息。

生成快照文件之后,回调 SaveSnapshotDone#run 方法,该方法以异步的方式将请求委托给 SaveSnapshotDone#continueRun 方法执行。

SaveSnapshotDone#continueRun
-> SnapshotExecutorImpl#onSnapshotSaveDone记录快照元数据信息,更新已经被快照的 logIndex 和 term 状态值,更新 LogManager 状态,并将本地已快照的日志剔除。

快照数据除了可以由本地生成,也可以是从 Leader 节点复制而来,如果从远端复制过来的快照数据相对于本地更新,则应该忽略本地生成快照文件的结果。SnapshotExecutor 定义了 SnapshotExecutorImpl#lastSnapshotIndex 和 SnapshotExecutorImpl#lastSnapshotTerm 两个字段用于记录最近一次快照对应的 logIndex 和 term 值,所以当生成快照成功之后需要更新这两个状态值。此外,既然相应的数据已经被快照,则表示对应的原生日志文件可以从本地存储系统中删除,从而节省存储空间。这一过程由 LogManager#setSnapshot 方法实现,该方法会对本地的日志数据执行截断处理。

3.安装快照

Replicator 期望给目标 Follower 节点复制日志数据时发现对应 logIndex 的数据已经变为快照文件,所以需要先给目标 Follower 节点安装快照。

Replicator#installSnapshot

  • 1)如果当前正在给目标 Follower 或 Learner 节点安装快照文件,则直接返回;
  • 2)否则,构造安装快照 InstallSnapshot RPC 请求对象,除了填充基本的状态数据外,其中还包含快照的远程访问地址,以及快照的元数据信息;
  • 3)向目标节点发送安装快照的请求。

Follower 节点对于 InstallSnapshot 请求的处理过程,由 NodeImpl#handleInstallSnapshot 方法实现。

NodeImpl#handleInstallSnapshot

  • 1)会完成一些基本的状态校验(具体实现与处理 AppendEntries 请求基本相同)
  • 2)SnapshotExecutorImpl#installSnapshot
     2-1)registerDownloadingSnapshot()尝试注册一个下载快照数据的 DownloadingSnapshot 任务;
      A)如果当前 SnapshotExecutor 已被停止,则放弃注册新的任务;
      B)否则,如果当前正在生成快照文件,则放弃注册新的任务;
      C)否则,校验安装快照请求中指定的 term 值是否与当前节点的 term 值相匹配,如果不匹配则说明请求来源节点已经不再是 LEADER 角色,放弃为本次安装快照请求注册新的任务;
      D)否则,校验本次需要安装的快照数据是否已在本地被快照,如果是则放弃注册新的任务;
      E)否则,尝试为本次安装快照请求注册新的下载快照文件任务,并开始下载快照文件。
     2-2)SnapshotStorage#startToCopyFrom从 Leader 节点下载快照文件到本地,并阻塞等待文件下载完成(下载快照文件的操作由拷贝器 LocalSnapshotCopier 完成,本质上是一个 RPC 交互的过程);
     2-3)SnapshotExecutorImpl#loadDownloadingSnapshot从本地加载下载回来的快照文件。

快照文件的生成过程时已知具体生成快照数据的过程由业务负责完成,而此处加载快照数据的过程同样也需要业务实现,因为这些快照数据都是执行业务指令所生成的特定时刻的数据状态备份。JRaft 同样会向状态机调度器 FSMCaller 发布一个 SNAPSHOT_LOAD 类型的事件,并由 FSMCallerImpl#doSnapshotLoad 方法负责处理此类事件。

FSMCallerImpl#doSnapshotLoad

  • 1)获取快照数据读取器SnapshotReader
  • 2)获取快照元数据信息
  • 3)本地已经应用的日志 logIndex 和 term 值相对于当前正在安装的快照更新,说明待加载的快照数据已经过期
  • 4)调用状态机 StateMachine#onSnapshotLoad 方法加载快照。FSMCaller 通过调用状态机 StateMachine#onSnapshotLoad 方法将快照数据透传给业务,并由业务决定如何在本地恢复快照所蕴含的状态数据。
  • 5)更新状态数据lastAppliedIndex和lastAppliedTerm 为快照数据的logIndex 和 term 值

SnapshotExecutor 在向 FSMCaller 发布 SNAPSHOT_LOAD 事件时会设置一个 InstallSnapshotDone 回调,用于感知加载快照数据的状态,如果操作正常则该回调会更新 SnapshotExecutor 本地的状态数据,包括最新被快照的 LogEntry 对应的 logIndex 和 term 值等。此外,还会调用 LogManager#setSnapshot 方法对本地已被快照的日志文件执行截断处理,以节省存储空间。

最后来看一下复制器 Replicator 对于安装快照请求 InstallSnapshot 的响应处理过程,由 Replicator#onInstallSnapshotReturned 方法实现。

Replicator#onInstallSnapshotReturned

  • 1)关闭快照数据读取器
  • 2)目标 Follower 节点运行异常
  • 3)目标 Follower 节点拒绝本次安装快照的请求
  • 4)目标 Follower 节点成功处理本次安装快照的请求,更新 nextIndex
  • 5)给目标节点安装快照失败,清空 inflight 请求,重新发送探针请求
  • 6)回调 CatchUpClosure

如果给目标 Follower 或 Learner 节点安装快照成功,则对应的复制器 Replicator 会更新下一个待发送的 LogEntry 索引值 Replicator#nextIndex 字段,并切换运行状态为 Replicate,接下去转为向目标 Follower 或 Learner 节点复制日志数据。

参考

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容