消息系统端到端Exactly-Once支持

本文涉及的产品
对象存储 OSS,OSS 加速器 50 GB 1个月
简介: 消息系统对于消息的写入和消费提供多种模式,如 at-most-once,at-least-once 和 exactly-once,本文探讨如何在一个消息提供中高效支持exactly-once的写入和消费

在消息系统中,消息的生成和消费通常支持三种模式: at-most-once,at-least-once 和 exactly-once。 这几种模式的区别主要在于当系统发生错误的时候,系统表现何种行为。前两种模式,非常容易理解,出错后不处理或者不停重试直到成功为止,对于exactly-once的模式,系统在出错的时候,则需要进行特殊处理来保证一条消息只被处理一次。

image

发生错误的场景

上图是常见的消息系统(如Kafka/Plusar/sls loghub等)的架构,消息(message)由生产者(Producer)发送至消息系统的接收者(Broker),Broker将Message持久化到特定分区(Partition)后,供消费者(Consumer)进行消费。在这个过程中,任意阶段都可能出错。

  • Broker 错误:Broker作为系统的主要组成部分,负责数据的接收和读取,当单个broker fail的时候,不同的系统表现行为也不同,可能会重新选举Leader或进行broker的failover,在这个过程中,消息的读写可能发生失败
  • Producer到Broker RPC错误 : Producer将数据发送至Broker后,需要收到Broker的ack才能确定消息写入成功,在Broker出错,或Producer到Broker网络异常等情况下,Producer可能无法收到ack信息,这时,Producer无法确认消息是否已经正确持久化,如果Producer忽略错误,这表现为at-most-once模式,进行重试直到成功,则可能是at-least-once
  • Producer :消息的生产者本身也可能会因为程序异常、机器宕机等行为导致异常,在Producer内存中尚未发送的消息,如果不做特殊处理,则会丢失
  • Consumer :作为消息的消费者,在发生错误的时候如何恢复消费的状态以及从哪个位置再次开始消费数据,则决定消费情况系统表现那种模式

写入模式下Exactly-Once

从上面的错误场景可以看到,错误在任意阶段都可能发生,要做到任意情况下完全的exactly-once写入代价极其昂贵,在线上大规模生产系统中,很难承受:

  • 每条消息有一个唯一的ID
  • Broker在接收到一条消息后,进行全局校验ID是否重复
  • Broker对于消息ID的校验和写入原子操作

在分布式场景下,做全局全量数据的原子排他性操作,成本无法接受的。那退而求其次,在一定限定条件下,则可以更高效达到exactly-once的效果。可以从以下两方面进行限定:

  1. Producer 发送的一条消息,在各种错误情况下,限定只到某个Broker下的确定Partition。这样消息无需做全局的校验,只需要在Partition级别即可;
  2. 单个Producer 到单个Partition的消息ID(Sequence ID)单调递增,这样虽然降低了单个Producer到单个Partition的吞吐(串行),但是每个Partition对于Producer消息的ID校验大大简化,只需要效验一个ID即可,无需维护历史所有ID

image

通过以上两个简化,在Partition级的exactly-once的写入操作,只需要额外一次HashMap的查询即可,而持久化的数据,也只会增加极少量的字段(ProducerID, Sequence ID)。

Broker Failover 处理

在Broker Failover的时候,必须将Broker内存中各个Producer当前写入的SequenceID完全恢复出来,才能保证数据exactly-once写入。在持久化的数据中,有每条消息的ProducerID和SequenceID信息,可以通过扫描持久化信息进行恢复,同时为了加快恢复速度,可以定期将Broker内存中的HashMap作为snapshot保存下来,在恢复的时候,首先恢复snapshot,然后只需要读取少量的信息完成ProducerID和SequenceID映射重建。

Producer Failover 处理

当Producer Failover的时候,对于部分数据源和SequenceID可映射的场景,可以根据ProducerID从Broker中获取最新的SequenceID,根据该ID对数据源进行重置(如Producer的数据源是文件,SequenceID 和文件行号能进行映射),Producer可以从上次最后写入成功的位置继续写入。

消费模式下 Exactly-Once

在完成exactly-once写入后,为了支持端到端的exactly-once处理, 同样需要消费端的配合,这里主要指在消费端failover时,如何确保每条消息只被处理一次。 虽然不少消息系统提供ack机制,消费者只需关系数据的处理即可,如Plusar提供的consumer会自动拉取消息供应用消费,应用只需要关心消息的处理,在处理完毕后,对消息进行ack即可。

Consumer consumer = client.subscribe(...);

while (true) {
    Message msg = consumer.receive();
    // Process the message...
    consumer.acknowledge(msg);
}

但是,这种简单的消费模式,无法支持exactly-once,核心在于消息的处理和ack是非原子操作。当消息处理完毕尚未进行ack时,consumer可能crash,重启后,这条消息将被重复消费(broker尚未收到这条消息的ack信息)。

因此,为了支持exactly-once消费,需要将消息处理的结果(通常使用状态表示)和消息的ID持久化到其他外部系统中,以确保failover能恢复到某个完全精确的状态继续消费,如flink使用的checkpoint机制,将flink内部某时刻状态以及消费的位置信息进行持久化。

即使如此,如果在消费过程中,会额外产生结果并写入其他下游系统时,如果这些系统不支持幂等操作,那么在failover时,consumer重复消费数据时,下游系统还可能看到at-least-once的结果(如消费信息进行短信报警的场景)。

以上是对于消息系统的端对端支持exactly-once的简单探讨,通过一定的条件限定,写入端支持相对容易,而消费端除了较复杂的checkpoint机制外,还依赖消费产出下游系统的支持。

相关实践学习
深入解析Docker容器化技术
Docker是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。Docker是世界领先的软件容器平台。开发人员利用Docker可以消除协作编码时“在我的机器上可正常工作”的问题。运维人员利用Docker可以在隔离容器中并行运行和管理应用,获得更好的计算密度。企业利用Docker可以构建敏捷的软件交付管道,以更快的速度、更高的安全性和可靠的信誉为Linux和Windows Server应用发布新功能。 在本套课程中,我们将全面的讲解Docker技术栈,从环境安装到容器、镜像操作以及生产环境如何部署开发的微服务应用。本课程由黑马程序员提供。     相关的阿里云产品:容器服务 ACK 容器服务 Kubernetes 版(简称 ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情: https://www.aliyun.com/product/kubernetes
目录
相关文章
|
存储 NoSQL Java
|
机器学习/深度学习 自然语言处理 并行计算
淘宝搜索中的深度语义模型:从理论到实践
淘宝搜索系统通过引入深度语义模型,极大地提升了搜索质量和用户体验。这些模型不仅能够准确理解用户的需求,还能够智能地匹配和推荐商品,为用户提供了一个更加便捷、个性化的购物环境。随着技术的不断发展和完善,淘宝搜索将会变得更加智能和高效。
|
自然语言处理 算法 搜索推荐
分词算法的基本原理及应用
分词算法的基本原理及应用
|
关系型数据库 MySQL Java
实时计算 Flink版操作报错合集之遇到删除操作时,出现Failed to deserialize data of EventHeaderV4 错误如何解决
在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。
594 5
|
存储 缓存 算法
【专栏】探讨分布式限流所面临的挑战以及目前业界常用的解决方案
【4月更文挑战第27天】在互联网时代,分布式限流是应对高并发、保护系统稳定的关键。它面临数据一致性、算法准确性和系统可扩展性的挑战。常见限流算法有令牌桶、漏桶和滑动窗口。解决方案包括使用分布式存储同步状态、结合多种算法及动态调整阈值。定期压力测试确保策略有效性。随着系统规模增长,限流技术将持续发展,理解并应用限流原理对保障服务质量至关重要。
385 3
|
NoSQL 关系型数据库 MySQL
车联网场景下海量车辆状态数据存储实践
随着通信技术、计算机技术的不断发展,移动通信正在从人与人(H2H)向人与物(H2M)以及物与物(M2M)的方向发展,“万物互联”的概念正在逐步覆盖到各行各业中,例如智能家居、智能农业、智能交通、智能物流等领域。目前,车联网技术已经先行一步,在行车安全、交通管理、生活服务等方面得到充分应用。 车联网技术包括了车辆终端、云端、无线通信等方面。车辆终端实时产生大量车辆状态数
2774 121
车联网场景下海量车辆状态数据存储实践
|
Prometheus Cloud Native NoSQL
【2023】Prometheus-Prometheus与Alertmanager配置详解
【2023】Prometheus-Prometheus与Alertmanager配置详解
1583 0
【2023】Prometheus-Prometheus与Alertmanager配置详解
|
存储 算法
【数据结构-零基础学习】线索二叉树(代码+图示+解析)
【数据结构-零基础学习】线索二叉树(代码+图示+解析)
1104 0
|
存储 NoSQL 定位技术
Redis Geo:解锁地理位置数据的新可能性
Redis Geo:解锁地理位置数据的新可能性
577 0
|
存储 SQL 缓存
淘宝高并发场景
淘宝高并发场景