Kafka
-
Kafka是一个高吞吐量,低延迟的分布式发布-订阅消息系统,单机吞吐量10万级,毫秒级延迟,用于大数据实时领域
吞吐量:系统在单位时间内处理请求的数量
GC吞吐量:用户程序执行的时间,GC时间越短,吞吐量越高
-
Kafka术语概念
- Broker:Kafka 服务器,负责消息存储和转发
- Topic:消息类别,Kafka 按照 topic 来分类消息,类似于数据库的表
- Partition:topic 的分区,一个 topic 可以包含多个 partition,topic 消息保存在各个partition 上,每个分区都有副本
- offset:消息在日志中的位置,可以理解是消息在 partition 上的偏移量,也是代表该消息的唯一序号
- Producer:消息生产者,Consumer:消息消费者
- Consumer Group:消费者分组,每个 Consumer 必须属于一个Group,是Kafka实现单播和广播两种消息模型的手段。对于同一个topic,每个group都可以拿到同样的所有数据,但是数据进入group后只能被其中的一个consumer消费
- Controller:保存着broker、topic、partition等元数据;负责分区Leader选举,管理集群Broker的上下线
- Controller的管理都是依赖于Zookeeper
-
Kafka中ZK的作用
- Kafka Broker集群的Controller选举,是通过ZK的临时节点争抢获得的(排他锁)
- ZK还保存着Kafka的元数据
- 在新版本里,Kafka把offset从ZK里移出来了
-
副本
- AR:分区中的所有副本
- ISR:所有与Leader副本保持一定程度同步的副本
- OSR:与Leader副本同步滞后过多的副本 AR = ISR + OSR
-
Kafka是pull还是push
用的Pull,因为不同的消费者有不同的消费能力,Push模式很难去适应这种情况,推的太快,Consumer可能撑不住,太慢,Consumer会有性能浪费,所以让Consumer根据自己的消费能力去拿数据,但是Broker没有消息的时候,Consumer会一直轮询,所以可以设置让Consumer阻塞直到有消息
-
消息中间件的作用
- 缓冲和削峰:有突发流量,下游可能扛不住,MQ在中间可以起到一个缓冲的作用,把消息暂存在队列中
- 解耦和扩展性:消息队列可以作为一个接口层,解耦重要的业务流程。只需要遵守接口约定,就可以进行扩展
- 冗余:一个生产者发布消息,可以被多个订阅topic的服务消费到
- 健壮性:消息队列可以堆积请求,所以消费端业务即使短时间down掉,也不会影响主要业务
- 异步通信:消息队列提供了异步处理机制,可以先把消息放队列里,需要的时候再去处理
-
为什么Kafka读写快
-
利用Partition实现并行处理:Kafka发布和订阅都要指定Topic,每个Topic又有多个Partition,不同的Partition又可以放到不同的节点上,其实是用的集群的优势去实现并行
-
顺序写:Kafka里的每个分区是一个有序的消息队列,编号从0开始,新消息是追加到Partition的末尾(就是一个文件)
-
顺序读:Consumer从Broker读数据的时候,他是带了偏移量的,他接着上次读的位置继续读
-
零拷贝:就是不需要CPU的参与,直接在内核Kernel里完成拷贝
- DMA:Direct Memory Access 直接存储器访问
- 不需要CPU参与,设备(硬盘,网卡)和系统内存(内核)之间数据传输的一个机制
-
比如这个消息生产过程(Producer->Broker),他是把网络数据持久化到磁盘上,传统模式下,是要经过4次拷贝
- 首先通过DMA Copy把网络数据拷贝到Socket缓存区
- 然后用户程序把Socket缓存里的数据拷贝到用户空间(CPU Copy)
- 接着用户程序再把这数据拷贝到内核缓存区(CPU Copy)
- 最后通过DMA Copy数据落盘
零拷贝就是把中间这两次经过用户空间CPU Copy去掉了
对Linux 操作系统而言,零拷贝技术依赖于底层的 sendfile() 方法实现。
对应于 Java 语言,FileChannal.transferTo() 方法的底层实现就是 sendfile() 方法
MMAP:将一个文件或者其它对象映射进内存,减少了一次CPU拷贝,直接Socket缓存区拷贝到内核缓存区
-
-
Kafka中的消息是否会丢失和重复消费?
-
这个问题可以从三个方面来考虑
-
首先是Producer端
- 消息发送方式默认是异步的(producer.type),可以配置成同步,Producer就能实时知道消息发送的结果
- 添加异步回调函数来监听消息发送的结果,如果发送失败,可以在回调中重试
- Producer本身提供了一个重试参数Retries,如果发送失败,会自动重试
-
然后是Broker端,提供了两个机制来确保消息的可靠性
-
Partition副本机制,是数据分区的高可用策略,每一个Paratition副本集会包含唯一的Leader和多个Follower,Leader专门去处理事务类型的请求,而Follower负责去同步Leader的数据
-
ACKS(request.required.acks)消息发送确认机制,他有几个值可以去设置
- 0:消息发出去,不管成不成功,如果这时候网络有问题,会有消息丢失
- 1:消息发出去,分区Leader接收成功,不管有没有同步到Follower,这时候Leader挂了,也会有消息丢失
- 所以ACKS可以设置成-1,就是消息写入Leader并且同步到Follower所有ISR副本之后再确认消息发送成功,就可以避免消息丢失
-
-
最后是Consumer端,经过Producer和Broker的设置和一些本身的机制,就可以保证消息可靠,不丢失,Consumer不太可能出现消息丢失的问题,倒是会可能出现重复消费
- 重复消费的根本原因在于:已经消费了数据,但是offset没有成功提交,比如触发了分区的Rebalance
- Rebalance:再均衡,有新消费者加入消费者组,或者有消费者被剔除,就会触发
- 所以要避免重复消费,Consumer要实现消费幂等
- 落表,数据库里设置主键或者唯一索引的方式
- offset手动提交,或者自定义存储offset(实现ConsumerRebalanceListener)
-
幂等性:多次执行所产生的影响均与一次执行的影响相同
-
-
-
Kafka事务
- Kafka有个组件Transaction Coordinator去实现事务,Producer就是通过跟这个组件交互获取一个全局唯一的事务TransactionID,将PID和TransactionID绑定,这样Producer重启,也没有影响;
- Transaction Coordinator 还把所有事务写入Kafka 的一个内部 Topic,这样即使整个服务重启,也可以恢复
-
为什么Kafka不支持读写分离?
如果Kafka是这种模式,可能会造成数据不一致,还有延迟问题,因为数据从主节点Leader同步到从节点需要时间,比如这时候数据更新了,主从节点还没来得及同步,Consumer就在从节点消费了,数据可能不一致;所以Kafka写入消息,读取消息的操作都是跟Leader副本进行交互的
-
Kafka中的选举
-
Controller控制器选举
通过ZK的临时节点来从多个Broker中选举出Controller,负责管理整个集群中所有分区和副本,如果某个分区的Leader副本出现故障了,控制器负责选举新的分区Leader
-
分区Leader选举
优先副本选举策略,必须满足三个条件:是AR的第一个副本&&副本在线&&副本在ISR列表中
-