一、介绍下kafka
kafka简介: kafka是一个分布式的流式处理平台,具有高吞吐、可持久化、可水平扩展、支持流数据处理等特性。
kafka功能:
- 消息系统:具备系统解耦、异步等功能的消息队列
- 存储系统:kafka可以把数据持久化到磁盘。得益于持久化和多副本机制。设置永久或者启动日志压缩
- 流处理平台:建立实时的数据通道来处理数据。
kafka架构:
producer
consumer
broker
topic
partition
leader副本
follower副本
二、kafka多副本是怎么管理的
AR: 分区中的所有副本,assigned-sync-replication
ISR: 所有与leader副本保持一致的副本(包括leader副本),in-sync-replication
OSR: 与leader副本同步相差较多的副本, out-sync-replication
leader负责维护和跟踪AR集合里所有follower副本的滞后状态,滞后过多放到OSR集合,进度赶上来放到ISR集合。
理论上来讲,只有ISR里的follower副本才有资格选举leader副本
副本leader选举:
谁来实施:controller负责具体实施
选举时间:leader副本所在broker挂掉后
谁来选举:ISR集合里的follower副本 优先副本就是说在AR集合中的第一个副本。理想情况下优先副本就是 leader 副本。优先副本选举就是促使优先副本成为leader副本,从而维护集群的负载均衡
三、生产者发送消息的模式
1. 发后即忘: 只管发送,不管发送是否成功。效率高,但是可靠性差。
2. 同步(sync): producer.send()返回一个Future对象,发送一个消息之前确认上一条消息成功
3. 异步(async): producer.send()传入一个回调函数,消息不管成功失败都调用这个函数,这样就算异步发送。这种情况对于消息失败是重试或者记录日志都取决于调用方
四、生产者发送消息的分区策略
1. 指定分区: 指定分区发送
2. 轮询: key为空,随机到某个分区
3. 通过key指定: key不为空,通过hash值映射到指定分区
4. 自定义策略: 实现org.apache.kafka.clients.producer.Partitioner
五、kafka支持读写分离么
不支持读写分离
kafka即使不支持读写分离也能在一定程度上实现负载均衡。 对于kafka架构来讲,实现读写分离有几个问题
- 节点之间数据不一致的问题
- 延时问题
六、kafka如何实现负载均衡
kafka如何实现负载均衡: 根据kafka主写主读的架构设计,是通过分区来实现一定程度的负载均衡,每个broker都有消费者拉取消息,每个broker都有生产者生产消息。
kafka负载均衡的问题: 会有一些情况导致不是那么均衡
- broker端分配不均:
- 生产者发送消息不均:
- 消费者拉消息不均
- leader副本切换不均:当主副本进行了切换或者分区副本进行了重分配,可能导致各个broker的leader副本分配不均。
七、怎么保证消息不丢失
可以拆成两个问题,1是什么情况下会丢消息,2然后根据这些情况尽可能保证不丢
producer角度:
1>. acks: 分区中多少副本收到消息才算成功,acks=-1或者acks=all,需要等待ISR中的所有副本都成功写入消息后才能收到服务端的响应,可靠性高。但是如果ISR中只有leader副本也可能有问题
2>. send重试 通过同步或者异步的方式发送,获取返回结果,进行失败重试
consumer角度:
手动提交位移: 当消费者消费到消息后,就会自动提交位移,但是如果消费出错就会丢失。可以先处理逻辑再手动提交位移,这样能避免丢失,也可能重复。
broker角度
leader副本所在的broker挂掉,follower副本需要重选举,但是没来得及同步,也会导致丢失 unclean.leader.election.enable = false,进度不达标不参与选举
kafka的消息传输一致性最多一次、最少一次、精准一次
1. 精准一次
定义:即使生产者重试发送消息,也只会让消息被发送给消费者一次
方案:手动提交位移,结合唯一id保证消息的原子性
2. 至少一次
定义:重试,消息可能被重复消费
出现:如果生产者接收ack超时或者收到了错误,就会重试。 如果broker恰好在消息已经成功写入后,在发送ack前出现故障,就会有两条一样的消息,消费者也会出现重复消费
3. 最多一次
定义:不重试,消息可能会没有被消费
怎么出现的:生产者接收ack超时或者收到了错误,不充实
kafka丢数据、重复消费、顺序消费问题
丢数据: 分为producer/consumer/broker三种
手动提交位移如何实现:
重复消费:
幂等性保障:
- 找出业务主键ID标记。比如订单号+订单状态。
- 【redis前置处理】在处理之前先去redis查一下是否存在该key,如果存在说明处理过了,直接丢掉。没处理过继续处理。
- 【mysql最终保障】依赖数据库的唯一索引做最终保证来实现幂等性
顺序消费
- 大宽表:多个字段多个状态,每一个状态单独分出一个独立字段,消息来时,只更新对应字段,消息只会存在短暂的消息不一致问题,状态最终还是一直的。
- 消息补偿机制:另一个进行消费相同topic的数据,消息落盘,延迟处理。将消息与DB进行对比,发现不一致,再重新发送消息至主进程。
- 发到相同的parition,因为一个partition是有一个consumer消费,能解决顺序问题。但是单线程容易堵塞。
八、如何增加消费端的消费能力
- 可以考虑增加topic的分区数,同时增加消费数量,消费者数=分区数
- 如果消费不及时,采用多线程消费,同时优化业务逻辑(同样的分区数,人家可以你为啥不可以)
九、消费者的分区分配策略
1. RangeAssignor分配策略(默认)
基于topic进行分配
2. RoundRobinAssignor分配策略
轮询是把partition作为最小单元
**3. StickyAssignor分配策略 **
分区的分配尽可能均匀;分区的分配尽可能和上次一致
kafka重平衡问题
十、controller是什么,有什么用
kafka集群有多个broker,其中有一个broker被选举为controlller,负责整个集群分区和副本的状态管理。
- 当某个分区的leader副本发生故障时,controller负责新的leader副本选举
- 当检测到某个分区的ISR集合发生变化时,controller告诉其他broker更新元信息
- 为某个topic增加分区数量时,controller负责分区的重新分配
十一、controller选举
kafka控制器信息存储在zookeeper的/controller节点,有三种情景会触发选举
- 集群从零启动
- /controller节点消失
- /controller节点数据发生变化
每个broker启动时,尝试读取/controller节点的brokerid的值,如果值不是-1,则表示已经有conteoller,放弃。
如果Zookeeper中不存在/controller 节点,或者这个节点的数据异常,那么就会尝试去创建/controller 节点,创建成功的那个 broker 就会成为控制器
十二、kafka为什么这么快
1. 顺序读写
磁盘分为顺序读写和随机读写,基于磁盘的随机读写比较慢,但是顺序读写很快,kafka就是基于顺序读写
2. page cache
利用操作系统本身的内存而不是jvm的内存
3. 零拷贝(可以深入了解)
直接将数据从内核空间的读写缓冲区拷贝到内核空间的socket缓冲区,然后再写到NIC缓冲区,避免了用户空间和内核空间的转换。
4. 分区分段
Kafka的message是按topic分类存储的,topic中的数据又是按照分区存储到不同broker节点。每个partition对应了操作系统上的一个文件夹,partition实际上又是按照segment分段存储的。
5. 批量读写
kafka读写消息是批量的而不是单条的,可以节约带宽
6. 批量压缩
所有的消息都会进行合理的压缩,减少IO损耗,提高IO速度。
十三、Kafka、RabbitMQ、RocketMQ的对比
性能: 消息中间件的性能主要体现在吞吐量。kafka吞吐量比Rabbit高出一两个数量级。kafka是单机QPS达到百万级,Rabbit万级别,Rocket单机写入TPS是7万/s
数据可靠性: kafka具有多副本机制,数据可靠性较高。rocketmq支持异步实时刷盘
服务可用性: kafka集群部署,结合分区和多副本设计,单节点宕机无影响。rabbitmq支持集群。rocketmq是分布式架构,可用性高
功能上: kafka和rabbitmq是比较主流的消息中间件,基本功能差不多,有一些特殊功能存在差异。 rocketmq在阿里内部使用比较多。
十四、实际中遇到的问题与解决
1. 消费积压问题
问题:消息中间件异常导致消息消费失败,大量kafka消费失败报警,经相关负责人了解到是kafka集群某节点内存故障导。业务侧观测到获取取元数据超时,消费失败,从而产生积压。
解决:
1> 如果集群异常或者未完全恢复,不要上线操作
2> kafka组件搞个开关,不要影响其他正常的业务
3> 上线服务可以将故障kafka节点从配置中删除,再进行上线重启
问题: 业务增长,生产者消息量超出消费者的消费能力
解决:
1> 提升单条消息的处理速度(优化逻辑/慢查询/日志不合理)
2> 对partition扩容增加partition数量
3> 消费端改为多线程并发消费
问题: 收到延迟报警,从kafka manager看,该topic有3个分区,两台机器来回变换的消费2号分区,消费进度没有任何变化;从 broker 端日志看,该消费组在频繁的进行rebalance
查看上下记录看懂设置了max.poll.records=20, max.poll.interval.ms=1000,也就是consumer一次最多拉20条消息,最长时间间隔1秒,必须处理完。否则,broker会认为消费者处理太慢而被踢出消费组,从而导致消费组rebalance.根据kafka机制,rebalance过程是不会消费消息的。所以两台机器轮流拉取消息,循环进行rebalance,消息就积压了。
解决: 消费者客户端减小 max.poll.records或者增大max.poll.interval.ms 默认5分钟500
十五、 RocketMQ的特点
- 亿级别消息的堆积能力,单个队列的百万级消息的累积容量
- 高可用性: Broker支持多master多slave的同步双写以及master多slave的异步复制模式,其中同步双写可保证消息不丢失
- 高可靠性: 生产者发送消息三种方式,同步、异步和单向,其中同步和异步都可以保证消息成功发送。 Broker刷盘策略:同步刷盘和异步刷盘,其中同步刷盘可以保证消息成功存储到磁盘。消费者的消费模式也有集群和广播消息两种,默认集群消费,如果集群模式消费着挂了,一个组中的其他消费着会接替其消费
- 支持分布式事务消息: 采用半消息确认和消息回查机制来保证分布式事务消息的。
- 支持消息过滤: 采用消费业务端的tag过滤
- 支持顺序消费: 消息在Broker中采用队列的FIFO模式存储,发送是有序的,保证消费有序即可。
- 支持定时消息和延迟消息: Broker中有定时消息的机制,消息发送到Broker,不会立即被Consumer消息,会等到一定时间才被消费,延迟消息同。