1 Kafka集群中几种机制
1.1 Controller1.2 Rebalance1.3 HW&LEO 2 Kafka线上问题优化
2.1 防止消息丢失2.2 防止消息的重复消费2.3 实现顺序消费2.4 解决消息积压问题2.5 实现延迟队列
1 Kafka集群中几种机制 1.1 ControllerKafka集群中总是有一个节点作为Controller,作为Controller的机器可以:
1,当某个Partition的leader副本出现故障时,由Controller为该Partition选择新的leader副本 Controller从ISR集合中选择靠前的节点作为新leader 2,当检测到某个分区的ISR集合发现变化时,由Controller负责通知所有节点更新元数据 3,当使用kafka-topics.sh脚本为某个Topic增加Partition数量时 同样是Controller负责让新Partition被其他节点感知到
Kafka集群中的Broker在ZK中创建临时序号节点时,序号最小的节点,即最先创建的节点作为Controller
1.2 Rebalance当Consumer没有指明Partition消费时,消费组中的Consumer和Partition的关系发生变化时
如消费组中某个Consumer挂掉,原先它消费的Partition在本消费组无人消费
就会触发rebalance机制,使得某个Consumer再去消费该Partition
同理增加新的Consumer也会触发rebalance机制
Consumer未指定Partition时,Consumer消费哪个Partition有3种策略:
1,range 通过公式计算哪个Consumer消费哪个Partition 如8个Partition给一个消费组消费 该消费组有3个Consumer 用类似sum/n的公式为Consumer较均衡分配Partition 2,轮询 Consumer轮着消费一个Partition 3,sticky 在触发了rebalance机制后 在Consumer消费原Partition不变的基础上调整 避免重分配带来的开销1.3 HW&LEO
首先看这样一个场景,Producer生产了三条消息发给了Broker0的Partition0分区
当Broker0完成Leader-Follower数据同步后,Consumer来消费时可以消费到第一二三条消息
接着Producer生产了第四条消息,这条消息刚被Broker0接收,还没有进行同步
此时Consumer想来消费,但此刻消费失败
控制这个过程中,有两个隐藏的数据,一个就是HW(高水位线),一个是LEO(log-end-offset)
每个Partition消息队列队尾位置用LEO标记,当LEO一致时,HW下降
此时主从的数据一致,通过这种方式来控制不丢弃数据
Consumer最多只能消费到HW所在位置的消息
当Broker0完成同步后,LEO相同,HW下降,Consumer消费4号消息成功
对于Leader新写入的消息,Consumer不能立刻消费,Leader会等待该消息被ISR中所有副本同步后更新HW
此时消息才能被Consumer消费,保证了即使Leader宕机,新消息也能在其他副本中找到,不丢失消息
消息丢失可能出现在两个过程中
1,生产者将将消息发送到Broker的某个Partition
为此可以借助ACK机制避免消息从生产者到Partition这个过程出现丢失的情况 将ACK回传策略置1或者-1可以防止消息丢失 如果要做到99.99%到达,将ACK设置为-1并将min.insync.replicas配置为分区备份数
2,消费者从Broker的某个Partition中poll消息
这个过程主要从提交offset的方式来解决 将自动提交改为手动提交即可2.2 防止消息的重复消费
消息的重复消费可能出现在如下的场景中:
Producer生产了一条消息发给了Broker,Broker收到后 由于网络抖动等因素
未在规定时间内回传ACK,导致Producer又重发了一次,
Broker收到了重复的消息,这样导致同一条消息被消费了两次
可能导致向DB中插入两条相同记录等后果
出现重复消费是某些业务场景不允许的,但是不能关闭重试机制
为此在消费者端解决非幂等性消费问题
幂等性即多次访问的结果是一样的,如get,post,delete是幂等的,post是非幂等的
1,在mysql中创建联合主键 防止创建多个相同记录 如将id(自增id)+order_id(uuid)设置为联合主键 联合主键要求两个id均不相同 当出现重复消息时,这两条消息插入记录的id不同 但uuid相同 第二条消息插入失败 避免了重复消费问题 2,使用分布式锁 保证只有一条记录能创建成功 如Redission.lock(order_id)2.3 实现顺序消费
顺序消费消息的场景有很多,如秒杀过程中订单的创建,支付与响应
即Producer生产了三条消息,创建订单消息,支付消息,响应消息
这三条消息的消费必须顺序进行,必须先创建好订单,再支付最后响应,不能乱序
上述图示中,由于网络或其他因素,消息2可能比消息1先消费,导致消费乱序
为此要实现顺序消费,先保证发送方同步发送 ACK策略不能设置为0
并指定一个Partition和一个ConsumerGroup来消费,但牺牲性能是不可避免的
Kafka的顺序消费场景不多,因为牺牲了性能,但如rocketMQ在这里有专门的设计
2.4 解决消息积压问题消息积压: 生产者在短时间内生产了大量消息 消费者由于机器性能不足或其他原因导致消费速度远赶不上生产者生产消息的速度 导致大量消息被存放在了Broker节点的物理磁盘中 等待被消费 堆积的消息越多,每次消者费通过offset寻找消息的过程耗时越长 导致Kafka集群性能越来越差 导致Broker磁盘被打满,依赖该Kafka集群的服务雪崩
为此解决的方案有:
1,消费者端使用多线程消费 2,创建多个消费组,多个消费者 部署到其他机器上一起消费 3,通过业务的架构设计,提升业务层面消费性能 代码优化消费速度 4,原先可能导致积压的主题称为Topic0 创建新的主题Topic1,并创建多个Partition 多个ConsumerGroup 当Topic0的生产者生产了大量消息时 Topic0的消费者将消息转发给Topic1 让Topic下的消费者集群辅助消费2.5 实现延迟队列
延迟队列的应用场景:
在创建订单成功后如果30min没有付款,需要取消订单
在Kafka中可以创建多个Topic,如Topic_5s,Topic_30min
代表延迟消费的队列,意为消息生产5s后消费,消息生产30min后消费
消费者不断轮询这些主题,判断拉取到的消息是是否已经符合消费要求(距该消息生产已过了多久)
如果符合 消费该消息, 不符合记录offset 终止本次消费 一段时间后继续轮询



