生产者消息分区机制原理剖析
Kafka的三级结构:主题 - 分区 - 消息
- 为什么要分区?
分区的作用是提供负载均衡的能力,不同的分区分布在不同的机器节点上,数据的读写都是针对分区的粒度进行。可通过增加机器来增加吞吐量
- 都有哪些分区策略
1、轮训策略
2、随机策略
3、消息键保序策略
分区是实现负载均衡以及高吞吐量的关键,故在生产者这一端就要仔细盘算合适的分区策略,避免造成数据倾斜,使得某些分区成为性能瓶颈。
Kafka副本机制详解
Kafka副本机制的好处:
- 提供数据冗余
同一个分区下的所有副本保存有相同的消息序列,这些副本分散保存在不同的Broker上,从而能对抗部分Broker宕机带来的数据不可用
数据同步机制:
同个分区下的不同副本基于领导者的副本机制进行数据同步,从副本只负责同步数据,不负责对外的读写工作。
原因:
- 方便实现“Read-your-writes”
- 方便实现单调读(Monotonic Reads)
主从分区实现数据同步的保证:In-sync Replicas机制
- Broker 端参数 replica.lag.time.max.ms 参数值设置的是主从同步的最长间隔
Kafka为什么那么快
- Kafka具有优秀的磁盘读写能力
- 批量量处理。合并小的请求,然后以流的方式进行交互,直顶网络上限。
- 请求采用多路复用的IO模型
Kafka请求是怎么处理
Kafka在TCP的基础上封装了一组请求协议,PRODUCR 请求用于生产消息,FETCH请求用于消费消息,METADATA请求是用于请求Kafka元数据
Kafka使用的是Reactor模式处理请求。
Reactor模式是事件驱动架构的一种实现方式,特别适合用于处理多个客户端并发向服务端发送请求的场景。
client —–> Reactor(Dispatcher) 公平分配 ——->read(网络线程池 ——>共享请求队列 ——>IO线程池 ) ——->decode —–> 网络线程池请求响应队列
幂等生产者和事务生产者是一回事吗?(kafka如何做到消息不会丢失,也不会被重复发送)
kafka如何做到消息不会丢失,也不会被重复发送?
Kafka提供消息不会丢失,但可能被重复发送的可靠性保障:
- 避免重复生产
1.创建幂等性Producer,当Producer发送了具有相同字段的消息之后,Broker会知道这些消息已经重复,并在后台进行舍弃。原理是就是经典的空间换时间的优化思想,Broker会在后台多保存一些字段,消息上报时会进行字段内容的核对。
幂等性 Producer的局限性:单分区幂等性、单会话幂等性
事务型 Producer:事务型 Producer 能够保证将消息原子性地写入到多个分区中。这批消息要么全部写入成功,要么全部失败。(类似于数据库的串行化)
生产者压缩算法
- 何时压缩:
生产者端 和 Broker端
Broker指定的压缩算法与生产者不一样时,Broker端需要先解压再依据自己的算法算法压缩。
- 何时解压
在consumer端获取的消息中有该消息的压缩算法
无消息丢失配置怎么实现
Kafka只对 “已提交的消息”的消息做有限度的持久保证。
- 可在一个或若干个Broker成功接收并写入日志文件后,会告诉生产者已提交
生产者程序丢失数据:Producer永远要使用带有回调通知的发送API
消费者程序丢失数据:维持先消费消息,再更新位移的顺序
还有一个解决办法是,多线程异步处理消费信息,Consumer 程序不要开启自动提交位移,而是要应用程序手动提交位移
总结:
- 使用peoducer.send(msg,callback)
- 设置acks=all
- 设置retries为一个较大的值
- 设置unclean.leader.election.enable=false
- 设置replication.factor=3
- 设置min.insynnc.relicas>1
- 确保replication.factor>min.insynnc.relicas
- 确保消息消费完成再提交
客户端都有哪些不常见但是很高级的功能
Kafka 拦截器分为生产者拦截器和消费者拦截器
- kafka拦截器的使用场景
Kafka 拦截器可以应用于包括客户端监控、端到端系统性能检测、消息审计等多种功能在内的场景
Java生产者是如何管理TCP连接
- 为何采用TCP?
- 从社区的角度看,在开发客户端时能够利用TCP本身提供的一些高级特性:多路复用请求以及同时轮询多个连接的能力
- 目前已知的 HTTP 库在很多编程语言中都略显简陋
- TCP连接何时创建?
- TCP 连接是在创建 KafkaProducer 实例时建立的
- 一个是在更新元数据后
- 在消息发送时
- TCP连接何时关闭?
- 用户主动关闭
- Kafka自带关闭(TTL)
Java 消费者如何管理TCP连接
- 何时创建TCP连接?
- TCP连接是在调用KafkaConsumer.poll 方法时被创建的
- 发起 FindCoordinator 请求时
- 连接协调者时
- 消费数据时
- TCP连接是在调用KafkaConsumer.poll 方法时被创建的
- 创建多少个 TCP 连接?
- 确定协调者和获取集群元数据
- 连接协调者,令其执行组成员管理操作
- 执行实际的消息获取
- 何时关闭连接?
- 手动调用 KafkaConsumer.close() 方法,或者是执行 Kill 命令
- Kafka 自动关闭,由消费者端参数 connection.max.idle.ms 控制的
消费者组到底是什么?
Consumer Group 是 Kafka 提供的可扩展且具有容错性的消费者机制
传统消息引擎模型:点对点模型和发布 / 订阅模型
点对点模型:消息一旦被消费,就会从队列中删除,而且只能被下游的一个consumer消费。
缺点:伸缩性(scalability)很差,因为下游的多个 Consumer 都要“抢”这个共享消息队列的消息
订阅模型: 允许消息被多个 Consumer 消费
缺点:每个订阅者都必须要订阅主题的所有分区。这种全量订阅的方式既不灵活,也会影响消息的真实投递效果
Kafka 仅仅使用 Consumer Group 这一种机制,却同时实现了传统消息引擎系统的两大模型
Rebalance 本质上是一种协议,规定了一个 Consumer Group 下的所有 Consumer 如何达成一致,来分配订阅 Topic 的每个分区
触发重平衡的条件:
1.组员数发生变更
2.订阅主题数发生变更
3.订阅主题的分区数发生变更
揭开神秘的“唯一主题”面纱
kafka自建位移主题保存consumer的消费位移
位移主题中的Key保存的内容格式:<Group ID,主题名,分区号 >
当 Kafka 集群中的第一个 Consumer 程序启动时,Kafka 会自动创建位移主题
如果位移主题是 Kafka 自动创建的,那么该主题的分区数是 50,副本数是 3
- 位移主题何时提交?
- 自动提交位移
- 手动提交位移
Kafka 提供了专门的后台线程定期地巡检待 Compact 的主题,看看是否存在满足条件的可删除数据
消费组重平衡能避免吗 ?
原理:
同一个consumer Group下的所有consumer实例在协调者组件的帮助下完成订阅主题分区的分配
Broker在启动时会开启相应的Coordinator组件,Broker所属的Coordinator组件与其可能不在同一个节点上。
Kafka 为某个 Consumer Group 确定 Coordinator 所在的 Broker 的算法:
- 确定由位移主题的哪个分区来保存该Group数据
- 找出该分区Leader副本所在的Broker,该Broker即为对应的Broker
重平衡的弊端:
- Rebalance影响Consumer端的TPS,因为重平衡期间消费者不可用
- Rebalance很慢,业务将长时间不可用
在真实的业务场景中,很多Rebalance都是计划外的或者说是不必要的。
触发重平衡的条件:
1.组员数发生变更
2.订阅主题数发生变更
3.订阅主题的分区数发生变更
后两个都是运维层面的不可避免,但是组员数目可以避免。
避免方式:
- 避免consumer未能及时发送心跳而导致被剔除
- 避免consumer消费时间过长
Kafka消息位移提交
Consumer 需要为分配给它的每个分区提交各自的位移数据
位移提交的语义保障是由你来负责的,Kafka 只会“无脑”地接受你提交的位移
从用户的角度来说,位移提交分为自动提交和手动提交;从 Consumer 端的角度来说,位移提交分为同步提交和异步提交
Kafka提供的提交位移的方法:
- 自动提交位移:
可能会出现重复消费
手动提交位移:
同步提交位移,提交过程中,consumer会处于阻塞状态,知道远端的Broker返回提交结果
异步提交位移,异步提交过程失败,重试并没有意义 因为消费的位移已经不是最新值
所以实际实践需要commitAsync() 避免程序阻塞,Consumer 要关闭前,我们调用 commitSync() 方法执行同步阻塞式的位移提交。
精细化管理位移
CommitFailedException异常怎么处理?
当消息处理的总时间超过预设的 max.poll.interval.ms 参数值时,Kafka Consumer 端会抛出 CommitFailedException 异常。
处理:
- 缩短单条消息处理的时间
- 增加 Consumer 端允许下游系统消费一批消息的最大时长
- 减少下游系统一次性消费的消息总数
- 下游系统使用多线程来加速消费
多线程开发消费者实例
Kafka Java Consumer 设计原理
Kafka Consumer 是双线程的设计,分为用户主线程和心跳线程
原因:
1.老版本consumer的每个实例都为所订阅的主题分区创建对应的消息获取线程,同时也是阻塞式的,Consumer 实例启动后,内部会创建很多阻塞式的消息获取迭代器,但在很多场景下,Consumer 端是有非阻塞需求的,社区为新版本设计了单线程+轮询的机制
2.单线程的设计能够简化 Consumer 端的设计。Consumer 获取到消息后,处理消息的逻辑是否采用多线程,完全由你决定。
Kafka 多线程方案:
Kafka Consumer 类不是线程安全的 (thread-safe)。所有的网络 I/O 处理都是发生在用户主线程中,因此,你在使用过程中必须要确保线程安全。
- 消费者程序启动多个线程,每个线程维护专属的 kafka Consumer实例,负责完整的消息获取、消息处理流程。
- 消费者程序使用单或多线程获取消息,同时创建多个消费线程执行消息处理逻辑
消费者组消费进度监控怎么实现?
监控Kafka的滞后程度 Lag
有三种方法监控:
- Kafka自带命令
- Kafka Java Consumer API
- 使用 Kafka 自带的 JMX 监控指标
消费者组重平衡全流程解析
依赖消费端的心跳线程来通知其他消费者实例,当需要发生重平衡时,Broker会把需要重平衡的信号封装至心跳上报的响应体中。
重平衡流程:
- 新成员加入:
- 新成员分别发送JoinGroup 请求和 SyncGroup 请求把组员信息发送给调解者,由协调者作为节点的分配
- 组员主动离组:
- 流程基本同新成员加入
- 组员崩溃离组:
- 靠心跳线程检测组员状态,由协调者发起重平衡
Kafka控制器
运行时只能有一个Broker作为控制器,第一个在zookeeper中创建controller节点的Broker会被指定为控制器
控制器职责:
- 主题管理(创建、删除、增加分区)
- 分区重分配
- Preferred领导者选举
- 集群成员管理(新增Broker 、Broker主动关闭、Briker宕机)
- 数据服务
控制器单点故障转移由zookeeper的watch功能保证通知
关于高水位和Leader Epoch
Kafka用高水位来表示Kafka中的消息位移,位移值小于高水位的表示已提交的数据,高于高水位的数据表示未提交信息,不能被消费者消费。
高水位的作用:
1.定义消息可见性,即用来标识分区下的哪些消息是可以被消费者消费的
2.帮助Kafka完成副本同步
管理和监控 skip
Kafka Stream与其他流处理平台的差异在哪
- Kafka Stream最大的特色就是它不是一个平台,至少它不是一个具备完整功能的平台
- 从应用部署方面来看,Kafka Stream倾向于将部署交给开发人员来做,而不是自己实现
- Kafka Stream只支持与Kafka的集群的交换
- Kafka Stream依赖Kafka的协调功能提供高容错性和高伸缩性
Kafka Stream与consumer的区别是Kafka Stream是实时流处理组件,提供了很多算子,可以实现更多复杂的业务