kafka如何保证消息有序

两种方案:
方案一,kafka topic 只设置一个partition分区
方案二,producer将消息发送到指定partition分区
解析:
方案一:kafka默认保证同一个partition分区内的消息是有序的,则可以设置topic只使用一个分区,这样消息就是全局有序,缺点是只能被consumer group里的一个消费者消费,降低了性能,不适用高并发的情况
方案二:既然kafka默认保证同一个partition分区内的消息是有序的,则producer可以在发送消息时可以指定需要保证顺序的几条消息发送到同一个分区,这样消费者消费时,消息就是有序。
producer发送消息时具体到topic的哪一个partition分区,提供了三种方式
1)指定分区
2)不指定分区,有指定key 则根据key的hash值与分区数进行运算后确定发送到哪个partition分区
3)不指定分区,不指定key,则轮询各分区发送

kafka如何保证消息有序

1. kafka有个offset的概念,当每个消息被写进去后,都有一个offset,代表他的序号,然后consumer消费该数据之后,隔一段时间,会把自己消费过的消息的offset提交一下,代表我已经消费过了。下次我要是重启,就会继续从上次消费到的offset来继续消费。
2. 但是当我们直接kill进程了,再重启。这会导致consumer有些消息处理了,但是没来得及提交offset。等重启之后,少数消息就会再次消费一次。
3. 其他MQ也会有这种重复消费的问题,那么针对这种问题,我们需要从业务角度,考虑它的幂等性。

kafka查找数据问题

1.kafka采用了分片和索引机制,将每一个partition分为多个segment。每个segment对应两个文件x.log和x.index 。这些文件位于一个文件夹下,规则为:topic名称+分区区号。

2. 先通过二分查找法定位到数据位于那个index中,然后找到数据行,数据行中记录的是消息的地址和消息的大小。

kafka分区顺序消费 kafka 分区有序_kafka

kafka分区
  1. 分区利于扩展和提高读写。
kafka数据可靠性保证
  1. 为了保证producer发送的数据,能可靠的发送指定的topic,topic的每个partion收到producer的数据后,leader和follower数据同步之后,会发送ack确认消息,如果producer收到确认ack,才会进行下一轮的发送。
  2. leader和follower数据同步方案
  • 描述
  • Propagate 消息
  • 通过zookeeper先知道leader在哪一台机器上,然后produce将消息发送到leader上,Follower 在收到该消息并写入其 Log 后,向 Leader 发送 ACK。一旦 Leader 收到了 ISR 中的所有 Replica 的 ACK,该消息就被认为已经 commit 了,Leader 将增加 HW 并且向 Producer 发送 ACK。
  • ACK 前需要保证有多少个 Replica 已经收到该消息
  • Leader 会跟踪与其保持同步的 Replica 列表,该列表称为 ISR(即 in-sync Replica)。如果一个 Follower 宕机,或者落后太多,Leader 将把它从 ISR 中移除。**Kafka 的复制机制既不是完全的同步复制,也不是单纯的异步复制。**完全同步复制要求所有能工作的 Follower 都复制完,这条消息才会被认为 commit,这种复制方式极大的影响了吞吐率(高吞吐率是 Kafka 非常重要的一个特性)。而异步复制方式下,Follower 异步的从 Leader 复制数据,数据只要被 Leader 写入 log 就被认为已经 commit,这种情况下如果 Follower 都复制完都落后于 Leader,而如果 Leader 突然宕机,则会丢失数据。
  • ACK 前需要保证有多少个 Replica 已经收到该消息
  • 1、等待ISR中任一Replica恢复,并选它为Leader等待时间较长,降低可用性
    或ISR中的所有Replica都无法恢复或者数据丢失,则该Partition将永不可用
    2、选择第一个恢复的Replica为新的Leader,无论它是否在ISR中
    并未包含所有已被之前Leader Commit过的消息,因此会造成数据丢失
    可用性较高
  • Data Replication如何处理Replica恢复
    • leader挂掉了,从它的follower中选举一个作为leader,并把挂掉的leader从ISR中移除,继续处理数据。一段时间后该leader重新启动了,它知道它之前的数据到哪里了,尝试获取它挂掉后leader处理的数据,获取完成后它就加入了ISR。
数据可靠性保证,ack应答机制
  1. acks=0 只发消息,不关心有没有收到。
  2. acks=2 等待broker的ack,producer写入硬盘后就发送ack。如果follower没有同步,会发生丢数据
  3. acks=-1 主从同步完成才发送ack。
    acks=-1 如果follower都不在isr的时候,会造成数据丢失。如果在leader和follower同步完成之后,leader挂掉,会发送数据重复的情况。
  4. hw数据消费一致性
  5. kafka分区顺序消费 kafka 分区有序_重启_02


kafka分区顺序消费 kafka 分区有序_kafka_03

Partition Recovery机制

每个Partition会在磁盘记录一个RecoveryPoint, 记录已经flush到磁盘的最大offset。broker fail 重启时,会进行loadLogs。 首先会读取该Partition的RecoveryPoint,找到包RecoveryPoint的segment及以后的segment, 这些segment就是可能没有 完全flush到磁盘segments。然后调用segment的recover,重新读取各个segment的msg,并重建索引。这样做的优点:

  1. 以segment为单位管理Partition数据,方便数据生命周期的管理,删除过期数据简单
  2. 在程序崩溃重启时,加快recovery速度,只需恢复未完全flush到磁盘的segment
  3. 通过index中offset与物理偏移映射,用二分查找能快速定位msg,并且通过分多个Segment,每个index文件很小,查找速度更快。
Partition Replica同步机制
  1. Partition的多个replica中一个为Leader,其余为follower
  2. Producer只与Leader交互,把数据写入到Leader中
  3. Followers从Leader中拉取数据进行数据同步
  4. Consumer只从Leader拉取数据
    ISR:所有不落后的replica集合, 不落后有两层含义:距离上次FetchRequest的时间不大于某一个值或落后的消息数不大于某一个值, Leader失败后会从ISR中选取一个Follower做Leader
数据可靠性保证

当Producer向Leader发送数据时,可以通过acks参数设置数据可靠性的级别:

  1. 0: 不论写入是否成功,server不需要给Producer发送Response,如果发生异常,server会终止连接,触发Producer更新meta数据;
  2. 1: Leader写入成功后即发送Response,此种情况如果Leader fail,会丢失数据
  3. 1: 等待所有ISR接收到消息后再给Producer发送Response,这是最强保证仅设置acks=-1也不能保证数据不丢失,当Isr列表中只有Leader时,同样有可能造成数据丢失。要保证数据不丢除了设置acks=-1, 还要保 证ISR的大小大于等于2
  4. request.required.acks:设置为-1 等待所有ISR列表中的Replica接收到消息后采算写成功; min.insync.replicas: 设置为大于等于2,保证ISR中至少有两个Replica Producer要在吞吐率和数据可靠性之间做一个权衡
数据一致性保证

一致性定义:若某条消息对Consumer可见,那么即使Leader宕机了,在新Leader上数据依然可以被读到

  1. HighWaterMark简称HW: Partition的高水位,取一个partition对应的ISR中最小的LEO作为HW,消费者最多只能消费到HW所在的位置,另外每个replica都有highWatermark,leader和follower各自负责更新自己的highWatermark状态,highWatermark <= leader. LogEndOffset
  2. 对于Leader新写入的msg,Consumer不能立刻消费,Leader会等待该消息被所有ISR中的replica同步后,更新HW,此时该消息才能被Consumer消费,即Consumer最多只能消费到HW位置
  3. 这样就保证了如果Leader Broker失效,该消息仍然可以从新选举的Leader中获取。对于来自内部Broker的读取请求,没有HW的限制。同时,Follower也会维护一份自己的HW,Folloer.HW = min(Leader.HW, Follower.offset)
什么是消费者组
  1. 既然是一个组,那么组内必然可以有多个消费者或消费者实例(consumer instance),它们共享一个公共的ID,即group ID。组内的所有消费者协调在一起来消费订阅主题(subscribed topics)的所有分区(partition)。当然,每个分区只能由同一个消费组内的一个consumer来消费。(网上文章中说到此处各种炫目多彩的图就会紧跟着抛出来,我这里就不画了,请原谅)。个人认为,理解consumer group记住下面这三个特性就好了:

    (1) consumer group下可以有一个或多个consumer instance,consumer instance可以是一个进程,也可以是一个线程
    (2) group.id是一个字符串,唯一标识一个consumer group
    (3) consumer group下订阅的topic下的每个分区只能分配给某个group下的一个consumer(当然该分区还可以被分配给其他group)
    (4) 一个消费者可以消费多个分区里的数据,一个分区里的数据只能被一个消费者消费。当消费者个数发生变化时,分配消费重新分配。(组名+主题+分区)。
    (5)不同组可以同时消费同一个分区。

消费者位置(consumer position)

消费者在消费的过程中需要记录自己消费了多少数据,即消费位置信息。在Kafka中这个位置信息有个专门的术语:位移(offset)。很多消息引擎都把这部分信息保存在服务器端(broker端)。这样做的好处当然是实现简单,但会有三个主要的问题:1. broker从此变成有状态的,会影响伸缩性;2. 需要引入应答机制(acknowledgement)来确认消费成功。3. 由于要保存很多consumer的offset信息,必然引入复杂的数据结构,造成资源浪费。而Kafka选择了不同的方式:每个consumer group保存自己的位移信息,那么只需要简单的一个整数表示位置就够了;同时可以引入checkpoint机制定期持久化,简化了应答机制的实现。

如何重新消费一个主题

换一个组,同时设置auto.offset.reset 为earlist

producer发送消息时具体到topic的哪一个partition分区,提供了三种方式
1)指定分区
2)不指定分区,有指定key 则根据key的hash值与分区数进行运算后确定发送到哪个partition分区
3)不指定分区,不指定key,则轮询各分区