目录
| 语义类型 | 解释 | 特点 | 适用场景 |
|---|---|---|---|
| At Least Once | 系统保证消息至少会被处理一次,但可能会被多次处理(即重复处理)。 | - 消息不会丢失。 - 可能会重复处理消息,需要幂等机制来避免副作用。 |
适用于必须保证消息被处理的场景,如订单、付款等。 |
| Exactly Once | 系统保证每条消息只会被处理一次,不会有重复处理,也不会丢失消息。 | - 消息不会丢失。 - 不会有重复处理。 |
适用于需要强一致性、对重复和丢失消息无法容忍的场景,如金融交易。 |
| At Most Once | 消息最多会被处理一次,但可能根本不会被处理。如果消息在传递过程中丢失,系统不会重发。 | - 可能丢失消息。 - 不会有重复处理。 |
适用于对消息重复处理敏感但可以容忍消息丢失的场景,如告警系统。 |
| Best-Effort | 系统会尽力发送消息,但不保证一定成功。消息可能会丢失,可能被多次处理。 | - 可能丢失或重复消息。 - 没有强保证。 |
适用于对消息一致性要求不高的场景,如低优先级的日志系统。 |
| Eventually Once | 系统保证消息最终会被处理一次,虽然在传递过程中可能会多次尝试,但通过幂等机制确保最终只处理一次。 | - 可能多次重发。 - 保证最终一致性。 |
适用于可以容忍短期不一致但要求最终一致的场景。 |
| Zero or Once | 消息要么不被处理,要么只被处理一次,确保不会有重复处理的情况。 | - 可能丢失消息。 - 不会有重复处理。 |
适用于对消息精确度有要求但允许某些消息丢失的场景。 |
一些开源组件是如何实现这些语义的
在分布式系统中,消息的传递和处理需要考虑不同的交付语义(如 "at least once"、"exactly once" 等)。开源组件通过结合重试机制、去重算法、事务处理和幂等设计来实现这些语义。下面将以几种典型的开源组件为例,讲解它们是如何实现不同的语义的:
1. Kafka
Kafka 是一个分布式消息队列,支持 At Least Once 和 Exactly Once 语义。
实现 At Least Once 语义:
- 消息重试机制:Kafka 保证消息在 producer 成功发送并写入 broker 后,consumer 至少能接收到一次。如果 consumer 处理失败或宕机,系统会重新分发该消息,导致重复处理。
- 幂等性和偏移量控制:consumer 通过提交消息的偏移量 (offset) 来记录处理状态,保证即使消息被重发,也可以通过偏移量检查是否已处理过。
实现 Exactly Once 语义:
- 幂等性 Producer:Kafka 的 producer 支持幂等性,可以确保在发生网络问题或其他错误时,重复发送的消息不会被写入多次。
- 事务机制:Kafka 支持端到端的事务(transaction),结合 producer 和 consumer 的事务性操作,Kafka 保证每条消息只会被处理一次,而不会发生丢失或重复。
例子:
- Kafka 的
enable.idempotence设置可以启用幂等性。 transactional.id可以用于配置 Kafka 的事务性生产者。
这里可以参考(点击跳转即可)
1.3至少一次、精确一次
2. RabbitMQ
RabbitMQ 是另一个流行的消息代理,支持 At Least Once 和 At Most Once 语义。
实现 At Least Once 语义:
- 消息确认 (Acknowledgment):当消息发送到 consumer 后,consumer 需要对 RabbitMQ 确认消息已经被成功处理。如果没有确认(如 consumer 崩溃),消息将重新被发送,从而确保至少一次处理。
- 消息持久化:RabbitMQ 可以将消息持久化到磁盘,以防止消息因节点故障而丢失,从而进一步保障消息至少被处理一次。
实现 At Most Once 语义:
- 无消息确认机制:如果禁用了消息确认机制(即
auto-ack),RabbitMQ 会认为消息在发送后即被成功处理,不会等待 consumer 确认。这种情况下,消息可能会丢失,但不会被重复处理。 - 非持久化传递:如果消息不被持久化且 consumer 崩溃,消息将直接丢失。
3. Apache Flink
Apache Flink 是一个分布式流处理框架,支持 At Least Once 和 Exactly Once 语义。
实现 At Least Once 语义:
- 状态快照和重放机制:Flink 通过 checkpoint 机制定期保存应用程序的状态。如果任务失败,可以从最后一次保存的状态重新开始处理数据流,确保不会丢失数据。
- 重放数据:当任务从快照恢复时,数据源将从上次成功处理的点开始重新发送数据,因此可能会导致某些记录被重复处理。
实现 Exactly Once 语义:
- 两阶段提交 (Two-Phase Commit):Flink 通过两阶段提交协议实现端到端的事务处理。source(数据源)、operator(中间处理节点)和 sink(数据输出端)共同参与事务,确保每条数据只被处理一次。
- Kafka Connector:Flink 与 Kafka 结合使用时,Kafka 的事务机制可确保端到端的 "exactly once" 语义。
4. Apache Pulsar
Apache Pulsar 是一种云原生的分布式消息传递平台,支持 At Least Once 和 Effectively Once 语义。
实现 At Least Once 语义:
- 消息重试机制:Pulsar 的 producer 和 consumer 通过 ack(确认)机制保证消息至少会被处理一次。如果没有收到确认,Pulsar 会将消息重新发送给 consumer。
- 消息持久化:消息存储在 Pulsar 的持久化存储中,确保在节点或网络故障时不丢失数据。
实现 Effectively Once(类似 Exactly Once)语义:
- 幂等消费:Pulsar 通过幂等消费和去重机制,确保消息不会被多次处理。特别是在有重试的情况下,consumer 可以检查消息的唯一 ID 确认是否已经处理过。
- 事务性消息传递:Pulsar 提供对事务的支持,确保在多个 topic 或 producer/consumer 之间的消息传递实现一致性。
5. Redis Streams
Redis Streams 是 Redis 5.0 引入的消息流特性,支持 At Least Once 和 At Most Once 语义。
实现 At Least Once 语义:
- 消费者组(Consumer Group)与确认机制:Redis Streams 支持消费者组,每个消费者处理不同的消息。当消费者成功处理消息时,会向 Redis 发送确认 (
XACK) 消息。如果没有确认,消息将留在 pending 队列中,等待重新分配给其他消费者处理。
实现 At Most Once 语义:
- 无确认的简单消费:如果使用简单消费模式(不通过消费者组),消费者直接读取消息,而不需要任何确认机制,消息处理失败后不会被重新分配,这样可能导致消息丢失。
总结
开源组件通过以下机制实现不同的消息交付语义:
- 重试机制:确保消息不会丢失,但可能导致重复处理,实现 At Least Once 语义。
- 幂等性:通过检查唯一 ID 或者保存状态来避免重复处理,结合重试机制实现 Exactly Once 或 Effectively Once 语义。
- 事务处理:通过事务性机制,确保消息从发送到消费的整个链路中只有一次被处理,实现 Exactly Once 语义。
- 无确认处理:直接发送消息,不等待确认,从而实现 At Most Once 语义,但消息可能会丢失。
选择不同的语义时,需要根据具体场景权衡数据丢失、消息重复处理以及性能开销的需求。
wrwvPGa2iWERJyPSCB4HtS
No comments yet.