目录
通过学习胡夕老师的极客时间课程《kafka核心技术与实战学习》,结合个人理解整理了这篇博客。
Kafka 是消息引擎系统,也是一个分布式流处理平台(Distributed Streaming Platform)。Spark、Flink是实时流处理平台,只能实现框架内的精确一次处理语义,对于Kafka而言,所有的数据流转和计算都在 Kafka 内部完成,故 Kafka 可以实现端到端的精确一次处理语义。Kafka还能够被用作分布式存储系统。
一、kafka概貌
![](https://i-blog.csdnimg.cn/blog_migrate/06e271ea1cd66a3d4ee83726ca4c1995.png)
1、主题与分区
(1)每个主题分为若干分区,同一个分区存在多个副本,副本分为两类:领导者副本(Leader Replica)、追随者副本(Follower Replica)。副本们分散在不同的Broker上,保存了相同的消息序列,从而避免部分Broker宕机带来的数据不可用。
(2)追随者副本不能响应消费者和生产者的读写请求,优点:
① 实现Read-your-writes:如果读取追随者副本,可能读取不到最新消息
② 实现单调读(Monotonic Reads):如果读取追随者副本,各追随者副本拉取进度不一致,对于某消息,可能一会读到,一会读不到
(3)ISR 副本集合(In-sync Replicas)
①定义:由与leader保持同步的副本组成(ps:必然包含leader副本)
②同步的标准:broker端参数replica.lag.time.max.ms,即落后 Leader副本的最长时间间隔
(4)分区作用:实现负载均衡、高吞吐量
2、压缩与解压缩
(1)哪里压缩
① producer端:根据参数compression.type指定的压缩算法,执行压缩
② broker端:指定了与producer端不同的压缩算法;兼容老版本消费者,将v2版消息转成v1版本消息
(2)哪里解压缩
① broker端:对每个压缩过的消息集合执行解压缩,目的是对消息执行各种验证
② consumer端:根据消息集合指定的压缩算法,执行解压缩
3、消费者组
(1)consumer group
由1个或多个consumer实例组成,consumer实例可以是进程或线程;通过字符串group id唯一标识。
传统的消息引擎模型:点对点模型(特点:消息只能被下游的1个consumer消费,且被消费后,就会从队列中删除)、发布 / 订阅模型(特点:每个订阅者都必须要订阅主题的所有分区)
对kafka而言,如果只有1个consumer group,类似点对点模型(消息队列模型);如果有多个consumer group,类似发布/订阅模型。
(2)Rebalance重平衡
① 是什么:指的是将分区分配给同一consumer group下所有consumer的过程
② 触发时机
a、组成员数发生变更;
b、订阅主题数发生变更(比如使用正则表达式的方式订阅主题);
c、订阅主题的分区数发生变更。
③ 缺点
a、与JVM的垃圾回收时stop the world万物静止相似,所有 Consumer 实例都会停止消费,等待 Rebalance 完成,影响消费端TPS
b、所有Consumer实例共同参与,全部重新分配所有分区,对某个consumer而言,可能后续不再负责它之前负责的分区了,执行慢、效率低
④ 如何避免:注意参数session.timeout.ms、heartbeat.interval.ms、max.poll.interval.ms、GC 参数
⑤ 重平衡时,可能发生CommitFailedException
发生原因:消费者实例C1连续两次调用 poll 方法的时间间隔超过了参数值max.poll.interval.ms => 消费者组开启了Rebalance,消费者实例C1向某分区提交位移时,该分区重分配给了消费者实例C2。解决措施:
a、缩短单条消息处理的时间
b、增加 Consumer 端允许下游系统消费一批消息的最大时长:增大Consumer 端参数 max.poll.interval.ms 的值
c、减少下游系统一次性消费的消息总数:减小Consumer 端参数 max.poll.records
d、下游系统使用多线程来加速消费
发生CommitFailedException的另一种情况:同时出现了设置相同 group.id 值的消费者组和独立消费者,当独立消费者程序手动提交位移时,kafka会立即抛出 CommitFailedException 异常
4、位移主题
(1)3种消息格式
① 位移主题的 Key 保存 3 部分内容:<Group ID,主题名,分区号 >,消息体主要是保存了位移值;
② 保存 Consumer Group 信息的消息:用来注册 Consumer Group ;
③ 删除 Group 过期位移甚至是删除 Group 的消息:tombstone 消息,即墓碑消息,也称 delete mark;消息体是 null,即空消息体。
a. 何时写入删除 Group 消息:某个 Consumer Group 下的所有 Consumer 实例都停止了,而且它们的位移数据都已被删除时,会向位移主题的对应分区写入消息,表明彻底删除这个 Group。
(2)何时创建
第一个consumer程序启动时,kafka会自动创建位移主题,分区数=Broker参数 offsets.topic.num.partitions ,副本数=Broker参数 offsets.topic.replication.factor
(3)提交位移的方式
① 自动提交:consumer根据时间间隔参数auto.commit.interval.ms自动提交;保证不丢失消息,但可能重复消费
② 手动提交:设置consumer参数enable.auto.commit = false;consumer调用位移提交方法:consumer.commitSync等
a、手动同步提交commitSync(...):执行时,consumer处于阻塞状态,直到broker返回提交结果,影响整个应用程序的 TPS
b、手动异步提交commitAsync(...):立即返回,不阻塞,提供了回调函数(callback);不会自动重试
(4)位移主题的过期消息Compact
后台进程log cleaner定期巡检位移主题,执行Compact(压实、整理)
二、kafka重要参数
1、参数列表
模块 | 功能 | 参数名 | 作用 | 备注 |
broker端参数 |
| log.dirs | 存放日志片段的目录 | 逗号分隔的本地文件系统路径 |
| zookeeper.connect | 指定保存broker元数据的zk地址 | 逗号分隔的一组hostname:port/path列表 hostname:zk服务器的机器名或ip port:zk的客户端连接端口 /path:zk路径,kafka集群的chroot环境 | |
broker如何与 客户端程序或其它broker通信 | listeners | 一组监听器 告诉外部连接者要通过什么协议访问指定主机名和端口开放的 Kafka 服务 | 逗号分隔的<协议名称,主机名,端口号> | |
advertised.listeners | 这组监听器是 broker 用于对外发布的 |
| ||
topic管理相关 | auto.create.topics.enable | 是否允许自动创建 Topic | 建议false | |
unclean.leader.election.enable | 是否允许unclean leader选举 | unclean:落后太多的follower副本 true:允许落后太多的follower副本竞选leader 建议false | ||
auto.leader.rebalance.enable | 是否允许定期进行 Leader 选举
| 上一个leader表现良好,也可能被换;所有客户端都需要切换向新leader,代价高 建议false | ||
数据留存相关 | log.retention.{hours|minutes|ms} | 控制数据被保存多长时间 该类参数与log.retention.bytes作用在日志片段上,满足两者中任一,消息就会被删除 | 默认168hours=1周 ①如何实现:通过检查日志片段的最后修改时间(关闭时间) ②优选使用具有最小值的参数 | |
log.retention.bytes | 每个分区能够在磁盘保存的数据大小 | 默认-1,表示无限制使用磁盘空间 | ||
message.max.bytes | 限制能接收的、压缩后单个消息的大小 | 消费者客户端设置fetch.message.max.bytes需大于等于此值,以免读取消息阻塞 | ||
消息防丢失 | replication.factor | 分区副本数量 | 假设diff= replication.factor-min.insync.replicas,允许挂diff个副本挂掉 建议replication.factor=min.insync.replicas+1 | |
min.insync.replicas | 消息写入多少个副本算是“已提交” | |||
broker端topic级别参数(覆盖broker级别的参数设置) |
消息保存相关 | retention.ms | 某topic下保存时长 |
|
retention.bytes | 某topic下可占磁盘空间大小 | 默认-1,表示无限制使用磁盘空间 | ||
消息处理相关 | max.message.bytes | 限制某topic下可接收的、压缩后的单个消息大小 |
| |
producer端参数 | acks | 表示必须要有多少个分区副本接收到消息,生产者才会认为消息写入是成功的 | acks=0,表示producer不等待broker的反馈; 默认acks=1,表示leader副本成功接收消息;acks=all,所有副本成功接收消息 | |
retires | 当生产者发送消息到服务器失败,生产者重发消息的次数 | 如果生产者重试达到了这个次数,生产者会停止重试并返回错误 | ||
enable.idempotence | true:开启幂等生产者 |
| ||
transactional.id | 事务id |
| ||
consumer端参数 | auto.commit.interval.ms | 自动提交时间间隔 |
| |
enable.auto.commit | 是否开启自动提交 |
| ||
session.timeout.ms | consumer心跳超时时间 | 超过此时间broker未收到心跳,认为consumer已挂 | ||
heartbeat.interval.ms | 控制发送心跳请求的频率 |
| ||
max.poll.interval.ms | 限制consumer端两次调用poll方法的最大时间间隔 | consumer如果在限定时间内无法消费完 poll 方法返回的消息, consumer主动发起“离开组”的请求,coordinator开启新一轮 Rebalance |
2、如何防止消息丢失
(1)producer
① 使用带有回调通知的send方法:producer.send(msg, callback)
② 参数设置:acks=all(指定接收消息的副本数)、retries>0
(2)broker参数设置:
unclean.leader.election.enable = false
replication.factor >= 3
min.insync.replicas > 1(指定写入消息的副本数)
replication.factor > min.insync.replicas
(3)consumer参数设置:
enable.auto.commit=false(手动提交位移)
三、幂等生产者与事务生产者
参考:https://www.infoq.cn/article/kafka-analysis-part-8
![](https://i-blog.csdnimg.cn/blog_migrate/5314b0dc77a9092a267bfb6fa8e89806.png)
![](https://i-blog.csdnimg.cn/blog_migrate/8dfc589b1d1497a1d89541ac65159f20.png)
四、TCP连接