关于消息

2020-01-11  本文已影响0人  kar_joe

生产者根据消息key以及“路由策略”将消息发往对应topic分区,消息数据在broker服务端按照指定备份策略“持久化”保存起来,消费者不断从服务端拉取消息消费,并提交“消费位移”,整个过程需要关注消息的“重复消费”以及“丢失”问题。

路由策略

hash、轮询or自己实现
Java客户端默认的生产者路由策略的实现类org.apache.kafka.clients.producer.internals.DefaultPartitioner。默认策略为:如果指定了partition就直接发送到该分区;如果没有指定分区但是指定了key,就按照key的hash值选择分区;如果partition和key都没有指定就使用轮询策略。而且如果key不为null,那么计算得到的分区号会是所有分区中的任意一个;如果key为null并且有可用分区时,那么计算得到的分区号仅为可用分区中的任意一个。
注意kafka只保证分区层面的消息有序性,topic层面并不保证。

消息持久化

Kafka 只对“已提交”的消息(committed message)做“有限度”的持久化保证。
Topic分区Partion,每个分区又有多个副本Repication,消息在broker中持久化到日志文件,多个follower副本异步从leader副本中同步消息。
如何持久化:每一个topic的分区对应一个目录,该分区的消息追加写到该目录下日志文件,用户可以分配日志保留策略和切割策略。由于采用了顺序写,也就是WAL技术(类似mysql里redolog的使用),性能较高。

消费位移提交

每个消息在服务端对应一个位移Offset,服务端利用位移查找消息。另外消费者需要将自己消费位移在合适时机提交,下次消费时从上次消费位移之后继续消费,即使有宕机。
老版本位移提交依赖于zk,由于zk其实不适合频繁写入,所以新版本kafka,把位移管理重新实现;消费者将消费位移信息写入特殊topic :__consumer_offsets。位移主题的 Key 中保存 3 部分内容::<Group ID,主题名,分区号 >

消息丢失

  1. 生产者异步发送,消息还没发出时就挂掉
    需要关注异常回调
  2. 消息poll后就先提交位移,消息的处理过程中消费者挂掉
    消息处理完成才提交位移(也会引入重复消费)
  3. unclean配置为true,让落后的broker成为leader,丢失消息
    在可用性与数据一致性之间做出抉择,若不允许数据丢失,则unclean配置置为false。
    另外在生产端以及broker服务端合理配置,设置ack=all,并保证replication.factor > min.insync.replicas>1,保证数据一直冗余备份
  4. 老版本broker分区之间数据同步机制导致
    社区在 0.11 版本正式引入了 Leader Epoch 概念,来规避因高水位更新错配导致的各种不一致问题。

消息重复消费

  1. 网络问题导致生产端未收到broker回复的ack,引起重发
  2. 消费者消费过程中挂掉,未提交消费位移,导致重复消费
  3. 消费reblance,一个正在消费信息时reblance,消息被另一个消费者又消费
    可以采用幂等性producer,并且在消费端业务处理逻辑上也支持幂等性

消息交付可靠性保障

消息交付有三种语义:

  1. 最多一次
  2. 至少一次
  3. 精准一次
    kafka默认是至少一次语义,想要实现精准一次,需要保证幂等性。幂等性就是任意多次执行所产生的影响均与一次执行的影响相同。
    kafka提供了幂等性producer以及事务性producer。幂等性producer底层保证了消息去重,但是其只保证了单分区,单会话上的幂等性。kafka用事务性producer解决上述局限,Kafka 自 0.11 版本开始提供了对事务的支持,目前主要是在 read committed 隔离级别上做事情。它能保证多条消息原子性地写入到目标分区,同时也能保证 Consumer 只能看到事务成功提交的消息。
producer.initTransactions();
try {
            producer.beginTransaction();
            producer.send(record1);
            producer.send(record2);
            producer.commitTransaction();
} catch (KafkaException e) {
            producer.abortTransaction();
}

另外即使kafka保证了消息唯一性,在极端情况下消费端也还会有重复消费问题,消费代码逻辑也需要保证幂等性。
另外补充一点,RocketMQ也实现了事务,但是其事务更倾向于保证本地其他操作与发送消息操作之间原子性。kafka的事务更倾向于保证多条消息原子性地写入到目标分区。

需要注意的细节

上一篇下一篇

猜你喜欢

热点阅读