首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

一种提高微服务架构的稳定性与数据一致性的方法(2)

一种提高微服务架构的稳定性与数据一致性的方法(2)

把消息队列放入到主流程如果要把重要的业务逻辑挂在消息队列后面。必须要保证消息队列里的数据的完整性,不能有丢失的情况。所以不能是把消息队列的写入作为一个旁路的逻辑。如果消息队列写入失败或者超时,都应该直接返回错误,而不是允许继续执行。
Kafka 的稳定性和延迟时常不能满足在线服务的需要。比如如果要可靠写入三副本,Kafka 需要等待多个 broker 的应答,这个延迟可能会有比较大的波动。在无法及时写入的情况,我们需要使用本地文件充当一个缓冲。实际上是通过引入本地文件队列结合远程分布式队列构成一个可用性更高,延迟更低的组合队列方案。这个本地的队列如果能封装到一个 Kafka 的 Agent 作为本地写入的代理,那是最理想的实现方式。
保障数据库与队列的事务一致性需求是当数据库的事务成功时,消息一定要保证写入了队列里。如果数据库的事务失败,消息不应该出现在队列里。所以肯定不能先写队列,再写数据库,否则要让 Kafka 支持消息的回滚,这会是一个很麻烦的事情。那么就要防范这么两种情况
  • 数据库写入成功。然后写队列,但是队列写入失败。返回错误,让上游重试。但是上游可能会放弃,导致消息丢失。
  • 数据库写入成功。然后全机房断电了。
这两种情况下都会出现消息没有写入队列的情况。如何仅仅依靠 Kafka 和 Mysql 这两个组件,实现数据库与队列的事务一致性呢?构想如下
  • 所有请求,先写入到 write-ahead-queue 这个 topic。如果这个消息就写入失败,直接返回错误给调用方,让其重试。
  • 处理数据库事务
  • 如果数据库事务失败。则移动 write-ahead-queue 的 offset,代表这个请求已经被处理完毕。
  • 如果数据库事务成功。则接下来写 business-event-queue 这个 topic
  • 如果写入队列成功。则移动 write-ahead-queue 的 offset,代表这个请求已经被处理完毕。
  • 如果写入队列失败,返回成功给调用方。然后异步去重试写入 business-event-queue 这个 topic
  • 在数据库事务成功到消息写入到business-event-queue这个topic中间,write-ahead-queue 的 offset 都是没有被移动的。也就是如果这个过程被中断,可以从 write-ahead-queue 恢复回来。
  • 经过重试,最终 business-event-queue 写入成功。这个时候移动 write-ahead-queue 的 offset,标记这个请求被处理完毕
也就是说,通过引入 write-ahead-queue,以及控制这个 topic 的 offset 位置,来标记完整的分布式事务是否已经被处理完成。在过去,这个处理是否完成是以数据库的事务为标准的,没有办法保障数据库事务之后发生的事情的必然发生。

虽然看上去很复杂。但是这个连两阶段提交都不是,因为没有回滚的需求,只要数据库写入成功,消息队列写入无论如何都要成功。整个方案的关键是通过 write-ahead-queue 的写入和offset的移动这两个动作,标记了一个分布式事务的范围。只要这个过程没有完全做完,就会通过不断重试 write-ahead-queue 的方式保证其最终会被完整执行。
在没有 write-ahead-queue 的时候,我们的 RPC 执行过程是这样的

这个串行过程,因为没有保护,所以可能被中断,不能被确保完整执行。引入 write-ahead-queue 的目的就是让这个过程变得可靠
返回列表