Kafka消费顺序保证

Kafka消息顺序保障

 

 

Opticalix关注

0.0972019.02.19 17:35:07字数 874阅读 7,752

在实现事件流流经Kafka时遇到了这个问题,即如何满足消息按produce顺序去consume。

概念&问题

首先,了解Kafka中broker、partition、topic的概念。

  • Topic:特指Kafka处理的消息源的不同分类。可理解为字面意“主题”。
  • Partition:Topic物理上的分组,一个topic可以分为多个partition,每个partition是一个有序的队列。partition中的每条消息都会被分配一个有序的offset。
  • Broker:直译“经纪人”,Kafka集群包含一台或多台服务器,服务器则称为broker。

针对最小单位partition,其内部可以保证顺序,但跨partition不保证顺序。也就是说,对某一topic如果增加partition可以增加吞吐能力,但无法保证topic级别的消息的有序性。
好了,那就在broker上新建一个topic: test,producer规规矩矩顺序生产,consumer新起线程消费...但是结果却不如我之前期望的:

    Record Key:null, value:SmartHomeRawEvent{id=19, timestamp=1377986492, value=68.451, property=0, plugId=2, householdId=1, houseId=0}, partition:0, offset:28
    Record Key:null, value:SmartHomeRawEvent{id=20, timestamp=1377986493, value=33.291, property=1, plugId=2, householdId=1, houseId=0}, partition:0, offset:29
    Record Key:null, value:SmartHomeRawEvent{id=1, timestamp=1377986401, value=68.451, property=0, plugId=11, householdId=0, houseId=0}, partition:0, offset:30
    Record Key:null, value:SmartHomeRawEvent{id=2, timestamp=1377986402, value=19.927, property=1, plugId=11, householdId=0, houseId=0}, partition:0, offset:31

以上是输出结果节选,可以看出{id=19}'offset=28 > {id=1}'offset=30,这是怎么回事?

MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION

稍加Google发现另一个Producer的关键参数:max.in.flight.requests.per.connection,Kafka-2.0.0源码对此参数解释为:

The maximum number of unacknowledged requests the client will send on a single connection before blocking. Note that if this setting is set to be greater than 1 and there are failed sends, there is a risk of message re-ordering due to retries (i.e., if retries are enabled).

这参数我理解是控制同时最大请求数(unack),在>1时且开启了retry,遇到失败请求时,就有可能造成消息重排序。试着设成1,这次消费顺序正常了。

Idempotent Producer

事情还没完,Stack Overflow上对max.in.flight.requests.per.connection讨论中有人提出开启幂等生产者(Idempotent Producer),即enable.idempotence=true代替max.in.flight.requests.per.connection=1,可以解决ordering并保证Exactly Once语义,同时由于最大请求数在最新kafka版本中兼容<=5,还能保证更好的性能。
于是对"幂等"又做了进一步调查,幂等就是指重复操作造成的影响和一次操作一样。这里Idempotent Producer特指重复produce message时只会有唯一一个有效的message,从而保证了Exactly Once而不是At Least Once。注意开启幂等可能会覆盖retry/ack配置;幂等性同样只作用于同一partition。
参数解释如下:

When set to 'true', the producer will ensure that exactly one copy of each message is written in the stream. If 'false', producer retries due to broker failures, etc., may write duplicates of the retried message in the stream.
Note that enabling idempotence requires MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION to be less than or equal to 5, RETRIES_CONFIG to be greater than 0 and ACKS_CONFIG must be 'all'. If these values are not explicitly set by the user, suitable values will be chosen. If incompatible values are set, a ConfigException will be thrown.

看图也很容易理解加幂等的原理,增加了一个对用户不可见的PID,broker只会接收同一个PID&Partition下Seq+1的请求。

 

未开启幂等

 

开启幂等

实测了几次确实没有出现消费乱序,不失为较好的保证顺序的方法。实验规模较小,如有错误还请指正。

Ref:
https://hevodata.com/blog/kafka-exactly-once/
https://stackoverflow.com/questions/49802686/understanding-the-max-inflight-property-of-kafka-producer

 

 

<think> 首先,用户的问题是:"我想查找Kafka保证消费顺序性的方法 请问Kafka如何保证消费顺序性"。我需要基于用户提供的引用内容来回答。 用户提供了四个引用: - 引用[1]:讨论了Kafka如何保证消费顺序性,强调通过两种策略(可能是分区顺序全局顺序)来满足业务需求。 - 引用[2]:解释了Kafka默认按分区顺序存储读取,全局顺序较难实现。提到了按分区顺序消费。 - 引用[3]:指出Kafka保证单分区有序,跨分区无序;如果尝试全局顺序会降低性能。 - 引用[4]:详细描述了Kafka保证顺序消费的方法,包括分区内顺序、并发消费、实现策略如单分区或分区键,以及注意事项。 我的回答需要: - 使用中文。 - 结构清晰,帮助用户逐步解决问题。 - 引用的段落末尾自然地添加引用标识,例如[^1]。 - 确保回答真实可靠,基于提供的引用。 - 在回答后生成相关问题(§§相关问题§§)。 - 遵循系统级指令:行内数学表达式用$...$,独立公式用$$...$$。但在这个主题中,可能没有数学表达式,所以如果不需要,可以省略。 核心内容:Kafka如何保证消费顺序性。 从引用中总结: 1. **分区内顺序保证**:Kafka保证单个分区内的消息顺序生产消费[^3][^4]。这是因为每个分区是顺序写入读取的。 2. **跨分区顺序**:Kafka保证多个分区之间的消息顺序[^2][^3][^4]。因为分区可以并发处理,消费者组可以并行消费不同分区。 3. **实现策略**: - **使用单分区**:如果业务需要严格全局顺序,可以使用一个分区,但这会降低吞吐量可扩展性[^3][^4]。 - **分区键(Partition Key)**:在发送消息时,指定分区键(如基于某个键值),确保相同键的消息发送到同一个分区,从而保证这些消息顺序[^4]。 - **消费者独占分区**:在消费者组中,确保每个分区只由一个消费消费,这样可以保证分区内的顺序[^4]。 4. **注意事项**:全局顺序消费(所有分区有序)难以实现,因为会牺牲并发性,导致性能下降[^2][^3]。 回答结构: - 先简要介绍Kafka顺序保证原则。 - 然后分步解释如何保证顺序性: - 分区内顺序保证。 - 跨分区顺序的挑战。 - 实现策略。 - 注意事项。 - 引用标识:当引用具体内容时,在末尾添加[^n]。 - 最后生成相关问题。 生成相关问题(§§相关问题§§): - 基于主题,如:Kafka分区键如何工作?如何优化Kafka顺序消费Kafka顺序消费的局限性? 确保回答真实可靠:基于提供的引用,不添加外部知识。 </think>### Kafka如何保证消费顺序Kafka保证消息消费顺序性主要通过分区机制来实现。核心原则是:Kafka默认保证单个分区内的消息顺序性,但跨分区时无法保证全局顺序。下面我将逐步解释保证顺序性的方法、策略注意事项,帮助您全面理解。 #### 1. **分区内顺序保证** - Kafka在单个分区内严格保证消息顺序性。消息按生产顺序写入分区,消费者从该分区读取时,也会按相同顺序消费[^3][^4]。这是因为每个分区是一个有序的日志序列,写入读取操作都是顺序的。 - 例如,如果您发送消息到同一个分区: ```python # Python示例:发送消息到指定分区确保顺序 from kafka import KafkaProducer import json producer = KafkaProducer(bootstrap_servers=['localhost:9092'], value_serializer=lambda m: json.dumps(m).encode('ascii')) partition = 0 # 指定分区号为0 for i in range(10): message = {"key": i, "value": f"message-{i}"} producer.send('test-topic', message, partition=partition) producer.close() ``` 在这个例子中,所有消息都发送到分区0,因此消费者会按顺序处理`message-0`到`message-9`[^4]。 #### 2. **跨分区顺序的挑战** - Kafka不支持全局顺序消费(即所有分区消息有序),因为多个分区可以并行处理[^2][^3]。如果一个主题有多个分区,不同分区可能由不同消费者同时消费,导致消息乱序。 - 原因:全局顺序会牺牲并发性。例如,如果某个分区卡顿,其他分区也必须等待,系统性能会严重下降,Kafka因此设计为只保证单分区有序[^3]。 #### 3. **实现顺序消费的策略** 为了满足业务对顺序性的要求,可以采用以下策略: - **使用分区键(Partition Key)**: - 在发送消息时,指定一个键(如用户ID或订单ID),Kafka会根据键的哈希值将消息路由到同一个分区。例如: ```python # 发送消息时使用键(key)确保相同键的消息进入同一分区 message = {"user_id": "123", "data": "order-created"} producer.send('orders-topic', key=b'123', value=message) ``` 这样,所有键为`123`的消息都会分配到同一个分区,并按顺序消费[^4]。 - **单分区策略**: - 如果业务需求严格全局顺序,可以将主题设置为只有一个分区。但这会限制吞吐量可扩展性,只适用于低流量场景[^3][^4]。 - **消费者组管理**: - 在消费者组中,确保每个分区由一个消费者独占。Kafka会自动分配分区给消费者,避免多个消费者同时消费同一分区导致乱序[^4]。 #### 4. **注意事项** - **性能权衡**:保证顺序性可能影响并发性。使用多分区可以提升吞吐量,但牺牲全局顺序;单分区保证顺序,但性能较低[^3][^4]。 - **业务逻辑处理**:在消费者端,如果消息顺序性至关重要,建议通过分区键绑定相关消息到同一分区,或使用外部状态机来跟踪顺序[^1][^4]。 - **Kafka默认行为**:Kafka保证分区内顺序,不处理跨分区顺序。业务设计时应根据需求选择策略,避免不必要的性能开销[^2][^4]。 通过以上策略,Kafka能有效满足不同场景的顺序性需求,如订单处理或事件溯源系统,确保消息系统的稳定性可靠性[^1]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值