特点
- 存取消息
- 临时存储消息
应用场景
- 异步处理
- 系统解耦
- 流量削峰(kafka单机吞吐量10w+,mysql单机只有几千)
- 日志处理
模型
- 生产者、消费者
两种模式
- 点对点(一条消息只能被一个消费者消费)
- 发布订阅(一条消息被多个消费者消费)
kafka集群必须有zookeeper
topic(主题):逻辑结构,类似mysql表,我们要在一个主题上存取数据
RabbitMQ 更适合业务异步解耦
RocketMQ 更适合金融事务场景
Kafka 更适合高吞吐日志流与实时计算
消费者组:将若干个消费者组合在一起,共同消费kafka中topic的数据
消费者组中活跃消费者数量小于等于topic分区数量
一个topic的一个分区只能由一个消费者消费,防止顺序混乱
消费者从kafka拉取数据需要offset
poll() 机制能保证你的程序在没活干时优雅地睡觉,有活干时瞬间起跳。
使用异步方式生产消息
- 在发送消息出现异常时,能够及时打印出异常信息
- 在发送消息成功时,打印kafka的topic名字,分区id,offset
同步:send(消息对象) future.get():阻塞等待返回
异步:send(消息对象,回调函数)
重要概念
- broker
一个kafka集群通常由多个broker组成,这样才能实现负载均衡
无状态,通过zk来维护集群状态
一个kafka的broker每秒可以处理数十万次读写,每个broker都可以处理TB消息而不影响性能
Kafka幂等性
两个概念
- PID:每个Producer在初始化时,都会分配一个唯一PID,这个PID对用户透明
- Sequence Number:针对每个生产者(对应PID)发送到指定主题分区的消息都对应一个从0开始递增的Sequence Number
问题:
生产者 retry 导致消息重复写入
解决:
Kafka Broker 会为每个 (PID, Partition)
维护一个期望的 seq(expectedSeq)。
只有当消息的 seq == expectedSeq 时,
Broker 才会接收并写入消息。
分区和副本机制
生产者分区写入策略
- 轮询分区策略(默认)
使用最多的策略,最大限度保证所有消息分配到一个分区
如果生产消息时,key为null,则使用轮训算法均匀地分配分区 - 随机分区策略
- 按key分区分配策略
根据key的hashcode取模,可以保证同一个key放入相同的分区 - 自定义分区策略
乱序问题:
轮询分区策略会导致分区内有序,分区间顺序不保证
key分区可以缓解乱序问题,但会导致数据倾斜
实际中应该根据业务选择合适策略
消费者组的Rebalance机制
再均衡,确保消费者组下所有的consumer如何达成一致,分配订阅的topic的每个分区的机制
触发时机:
1、消费者组中消费者的个数发生变化
3、订阅的topic发生变化(本质还是分区改变了)
2、订阅的topic发生分区变化
再均衡不良影响:
消费者组中所有消费者都会协调在一起共同参与,kafka使用分配策略尽可能达到最公平的分配
过程会对消费者组产生非常严重的影响,所有消费者停止工作,直到再均衡完成
消费者分区分配策略
Range范围分配策略(默认)
确保每个消费者组的分区数量是均衡的(rangle范围分配策略针对每个topic)
RoundRobin轮询策略
按照字典序排序(topic和分区的hashcode进行排序),然后通过轮询方式逐个将分区以此分配给每个消费者
粘性分配策略
1、分区尽可能均匀
2、在发生再均衡的时候,分区的分配尽可能与上一次分配保持相同(非全局分配而是局部调整)
副本机制
冗余备份,当某个broker上的分区数据丢失时,依然保证数据可用性,因为其他的broker上的副本是可用的
生产者的acks参数
0:不等待broker确认,直接发送下一条数据,性能最高,可能存在数据丢失问题
1:等待leader副本确认接收后,才会发送下一条数据,性能中等
-1(all):等待所有副本已经将数据同步后,才会发送下一条数据,性能最差
根据业务选择合适参数
分区中有leader和follower概念,为了确保消费者消费的数据是一致的,只能从分区leader去读写消息,follower的事情就是同步数据
API
高级API
直接让kafka帮助管理、处理分配、数据管理
不控制offset,不能从指定位置读取
不能细化控制分区、副本、ZK等
低级API
编写程序自己控制逻辑
自己控制offset,能自定义实现功能,实现复杂
可以指定消费者的topic分区,消费特定数据分区
监控工具Kafka-eagle
监控管理工具
Kafka原理
分区的leader与follower
针对分区(partition)
自动将leader均匀地分配在不同broker中
leader负责读写,follower负责备份(容灾),还有重新选举
leader出现故障,会在follower会选举新leader
AR、ISR、OSR
leader挂掉之后需要选取新的leader
AR(assigned Replicas):分区的所有副本
ISR(In-sync-replicas):所有与leader副本保持一定程度同步的副本
OSR(Out-of-Sync-replicas):同步滞后过多的副本(或者挂掉的follow副本)
leader选举
如何确定某个partition是leader、哪个partition是follower:
随机选取,尽量均匀
某个leader崩溃了如何快速确定另外一个leader:
因为kafka吞吐量高、延迟低,选取一定要快
选取范围:ISR
controller:启动时在所有broker中选择一个controller
- 所有partition的leader选举都由controller决定
- controller会将leader的改变直接通过RPC的方式通知对应Broker
- 通过ISR,选择存活备份,否则随便选,如果备份全宕机,新leader为-1
leader负载均衡
preferred replica:优先副本
生产、消费数据工作流程
生产者优先找到对应分区的leader
先找leader,采用拉模式(默认从最新数据读取)
数据存储形式
- 一个topic由多个分区组成
- 一个分区由多个segment组成
- 一个segment由多个文件组成
- log:存储数据
- index:存储offset索引(稀疏索引)
- timeindxe:存储时间索引
LEO:log-end-offerset日志最后的offset(下一条待写入消息的offset)
定期清理
消息默认7天后开始清理,一次删除一个segment段的日志文件
kafka日志管理器,根据kafka配置,决定那些文件可以被删除
消息不丢失机制
生产者数据不丢失
通过多个副本,leader副本挂了,可以在ISR集合中快速选出新leader副本
生产者数据不丢失
ACK机制,设置为all(-1):保证所有副本数据同步后返回确认
发送数据两种方式:同步(等待ack返回)、异步(提供一个回调函数)
消费者数据不丢失
只要每个消费者记录好offset值即可
消息丢失:自动提交的offset容易出现(消费数据失败,offset已经变化(存在zk中))
重复消费:自动提交offset时,更新zk中offset失败
解决:使用低级api,直接手动管理offset,不将offset存入zk而是从mysql拉取(通过事务保证消息和offset的原子性)
消息传递的语义
at-most-once:最多一次(只管把数据消费到,不管有没有成功,可能会有数据丢失)
at-least-once:最少一次(有可能出现重复消费)
exactly-once:仅有一次(事务性的保障,保证消息有且仅被处理一次)
数据积压
虽然很快,但是网络拥堵,外部IO(消费者进程)也会导致积压,破坏数据实时性
问题一:partition不够
解决:增加partition,增加消费者
问题二:消费者消费慢
解决:一次拉取多条数据,批量操作;通过线程池处理任务
问题三:消费者挂了(比如fullGC)
解决:恢复消费者
问题四:Rebalance 导致暂停消费
解决:增量式rebalance(新版)
问题五:下游故障(mysql挂了,不提交offset)
解决:恢复mysql
避免:监控指标(出现积压赶紧排查问题)
kafka数据清理
日志删除:按照指定的策略直接删除不符合条件的日志
日志压缩:按照消息的key进行整合,有相同key的但有不同value值,只保留最后一个版本
混合模式
以segment为单位进行定期清理
定时删除日志:
- 基于时间的保留策略
默认保存7天(168小时) - 基于日志大小的保留策略
- 基于日志起始偏移量的保留策略
kafka事务
幂等性只能保证单次会话幂等性
生产者挂了,重试(新会话,新PID)
解决:引入全局唯一TransactionID
并将PID和TransactionID绑定
当生产者挂了,通过TransactionID拿到自己之前的PID
通过:Transaction coordinator组件管理
消费者事务:不保证,自己保证