FolkMQ v1.7.8

快照持久化策略

消息中间件的数据是时实变化的,如果保存行为数据,量大且费性能。所以,快照是比较好的方案。FolkMQ 的架构简单、数据结构少,非常适合快照。

快照只保存最新的全量状态,不留历史数据。

1、FolkMQ 的快照持久化时会做些什么?

快照采用逐行写入,逐行读取的方式;每一行为一条完整数据的 Json 格式。

  • 全量保存“订阅关系”为 subscribe-map.fdb。即主题的订阅关系映射,内容示例:
{"topic":"...","queues":["...","..."]}
{"topic":"...","queues":["..."]}
  • 全量保持每个“消费队列的数据”为 {topic}/{consumerGroup}.fdb。内容示例:
{"meta":"...","data":"..."}
{"meta":"...","data":"..."}

2、是怎么实现的?

FolkMQ 开放有 MqWatcher 接口,它可以观察 MqServer 所有的行为及相关数据。持久化是基于 MqWatcher 接口实现的。

  • 恢复快照

在 onStartBefore 时,同步恢复订阅关系;在 onStartAfter 时,异步恢复消费队列的数据。

  • 持久化快照

在 onStopAfter 时,会触发 onSave;在 onSave 时,会保存“订阅关系”和每个“消费队列的数据”。

3、什么时机会触发?

a) 停止时触发

在服务停止时,通过 Runtime.getRuntime().addShutdownHook 触发持久化。如果突然停电、强制杀进程之类的特殊情况,此策略会失效

b) 手动触发

在服务端控制台,有菜单可手动触发。如果明确知道会停电、或特殊维护,可以手动触发。心情不好也可以点一下。

c) 运行时自动触发

可以通过环境变量(方便 docker)或系统属性进行调整:

folkmq.snapshot:
  enable: true
  save900: 1
  save300: 10
  save100: 10000
  • 每 100 秒内,如果有 10000 个消息变化则触发
  • 每 300 秒内,如果有 10 个消息变化则触发
  • 每 900 秒内,如果有 1 个消息变化则触发

4、怎么保证可靠性?

同一份数据会存在三个文件(不保存历史数据):临时写文件(.fdb.tmp),快照文件(.fdb),备份文件(.fdb.bak)。写过程为:

  • 同步锁开始
  • 创建 *.fdb.tmp 文件,并写入数据
  • 如果顺利完成,删除 *.fdb.bak 文件
  • 如果顺利完成,将 *.fdb 文件更名为 *.fdb.bak
  • 如果顺利完成,将 *.fdb.tmp 文件更名为 *.fdb
  • 同步锁结束