Kafka基础知识(个人总结)

    声明: 1. 本文为我的个人复习总结, 并那种从零基础开始普及知识 内容详细全面, 言辞官方的文章
              2. 由于是个人总结, 所以用最精简的话语来写文章
              3. 若有错误不当之处, 请指出

消息队列:

作用(优点):

  1. 异步处理 使用微信 进行建行卡支付时, 如果没资金不足等问题, 就先给用户响应支付成功, 后续再进行真正的建行卡扣钱操作

    ​ 这样使响应速度较快, 系统可用性提高, 用户体验感更好

  2. 业务解耦 A系统不想直接对接下游的一堆系统BCD, 就让A发给MQ, 然后下游系统去消费MQ即可, 这样下游系统扩展了E系统, 也不会对A系统产生影响

  3. 流量削峰 双十一流量削峰填谷, tomcat根据自己能力去消费数据, 而不是一下子接收全部请求

  4. 一些业务的失败重试机制可以借助MQ完成

缺点:

  1. 系统的可用性降低
    很多服务都依赖于MQ,一旦MQ故障,系统崩溃

  2. 系统变复杂了

    中途经过MQ再到目的地, 没有直接到达目的地快。且MQ自身原因可能导致数据重复, 丢失, 乱序等

  3. 一致性问题
    异步处理时, 系统A给BCD发送,只有都成功才返回成功,结果BC成功,但是D失败,但是返回页面结果是成功; 这时需要后续D的几次重试或人工处理

两种模式:

  1. 点对点模式
  2. 发布/订阅模式(Kafka用的是这个)

Kafka是微批处理, 高吞吐; RabbitMQ特点是低延时

Kafka架构:

Kafka基础知识(个人总结)

特点:

  • ZooKeeper在Kafka集群里的作用:

    1. 存储 topic信息 & brokerId & controller等集群相关信息
    2. 负责管理集群broker的上下线
    3. 接收客户端的请求, 代理转发给Kafka Server;
    4. 维护Controller节点
      • 负责leader选举, Controller里写的是谁, 谁就是leader
      • 负责topic的创建工作
      • 负责更新metadata cache

    新版本ZooKeeper被移除了

  • 自带的位移主题_consumer_offsets用来保存Consumer消费topic时提交的offset

  • 消息共享性: 消费者组之间互不影响

  • 消息互斥性: 同组的消费者不能消费同一条消息, 消费者组内每个消费者负责消费不同分区的数据

  • 这里的leader不是Server, 而是副本

  • 在leader正常工作时, follower是不参与工作的

  • topic是逻辑上的概念, partition是物理上的概念

Kafka文件存储机制:

Kafka基础知识(个人总结)

为防止log文件过大导致数据定位效率低下,Kafka采取了segment分片和索引机制

segment命名特点:

  1. 文件夹的命名规则: topic的名称+分区序号
  2. index和log文件命名规则: 以当前segment的第一条消息的offset命名

索引 指向对应数据文件中message的物理偏移地址, 是稀疏索引

生产者端:

为什么要分区?

  1. 增加存储利用率, 每个partition可以通过调整以适应它所在的机器, 这样整个集群更能适应任意大小的数据
  2. 提高并发读写性能, 以partition为单位进行并行读写

分区的原则:

  1. 指明partition时, 就用指定的

  2. 没有指明partition值但有key时, partition值=hash(key)%partition数量

  3. 没有指明partition也没有key时, 轮询调度, 第一次调用时随机生成一个整数num,

    partition值=num%partition, 之后的每次都会++num自增

数据可靠性的保证:

可靠性 和 延迟 之间折中做权衡

AR: 分区的所有副本

  1. ISR: 与leader保持同步的follower集合

    1. 用作ACK应答:

      当ISR中的所有follower都完成数据同步之后,就会给producer发送ack

      如果follower长时间未向leader同步数据,则该follower将被踢出ISR

    2. 用作发生故障时选举新的leader:

      leader发生故障之后,就会从ISR中选举新的leader

    关于踢出ISR的条件:

    1. 在老版本里是 延迟时间 和 延迟条数, 一个条件满足即可加入ISR

      这样不好, 比如延迟条数阈值是10条, 而当一次性提交15条数据, 那么ISR中的节点此时与leader延迟条数超过10条, 会被踢出ISR; 那么等这些被踢出的机器开始同步完成数据后, 又被重新加入ISR; 这样频繁退出和加入, 浪费集群资源, 期间还要和ZooKeeper沟通通信, 浪费时间

    2. 所以在新版本里, 只有延迟时间这一个条件限制

  2. ACK应答机制:

    Producer端 acks参数配置:

    1. acks=0(At Most Once):producer不等待broker的ack, 相当于禁用ack

    2. acks=1:leader落盘成功后就返回ack

      ​ 如果发送完ack后, leader立马挂掉, 那数据就丢失了

    3. acks=-1(At Least Once):partition的leader和ISR里的follower全部落盘成功后才返回ack。

      ​ 如果在follower同步完成后,broker发送ack之前,leader发生故障,则会造成数据重复

  3. 故障处理

    高水位HW, 和LEO

    Kafka基础知识(个人总结)

    1. leader故障

      从ISR中选出一个新的leader,

      为保证多个副本之间的数据一致性, 其余的follower会先将自己高于HW的部分截掉, 然后从新的leader同步数据

    2. follower故障

      follower发生故障后会被临时踢出ISR,

      等到该follower恢复后,它会读取本地磁盘记录的上次的HW,并截取掉高于上次HW的部分,

      然后开始同步leader的数据

      等到该follower的LEO大于等于该partition的HW, 即follower追上leader之后, 就可以重新加入ISR了

    这只能保证副本之间的数据一致性,并不能保证数据不丢失 或 不重复

Exactly Once生产:

Exactly Once(精准一次性) = At Least Once(acks=-1) + 幂等性

启用幂等性:

Producer端 设置参数enable.idompotence=true,

结合Producer端事务, 并且Broker会把<PID, partition, SeqNumber>当作联合主键(以此做幂等去重),

可以跨分区, 跨会话(进程重启)

消费者端:

consumer采用pull(拉)模式 消费数据

push & pull:

A的数据发给B

  1. A push推给 B:

    ​ A更加劳累, B可以做自己的事, 被A主动通知时再处理A发来的数据

    ​ 优点: 获取到数据时效性更高, 延迟低

    ​ 缺点: 难适应 不同消费速率的消费者

  2. B pull拉 A
    B得不停每隔一小会就询问A是否有数据了, 对B来说很劳累

    ​ 优点: 容易适应 不同消费速率的消费者

    ​ 缺点: 因为是每隔一小会才询问, 所以时效性低, 延迟高

分区分配策略:

即确定partition由消费者组的哪个consumer来消费

  1. RoundRobin: 轮询策略
  2. Range: 记作num=partition个数/某个消费者组的消费者总个数, 然后每个消费者取num个, 最后若有剩余的分区 分配给其中一个消费者即可

可见RoundRobin比较均匀, 而Range会在除不尽的情况下 导致其中一个分区的负载重一点

offset的维护:

offset提交 参数设置:

auto.offset.reset:

  1. latest 接着上次消费的offset位置 继续消费
  2. earliest 从头开始消费
  3. none 自己维护提交offset

事务:

Producer事务:

能够保证消息原子性地写入到多个分区中, 这批消息要么全部成功, 要么全部失败

全局唯一的Transaction ID可保证事务 跨分区跨会话

Consumer事务:

可以设置事务的隔离级别read_committed(只读取已提交的数据) 和 read_uncommitted(可读取未提交的数据)

如果想实现精准一次性消费, 需要自己手动维护offset(借助MySQL的事务机制, 实现 消费数据 和 提交offset 要么都成功, 要么都失败)

  1. 先提交offset后消费,有可能造成数据丢失, 少消费

  2. 先消费后提交offset,有可能会造成数据的重复消费

消息重复 要 好于消息丢失, 因为即便重复了, 后续可以做去重

选举策略:

  1. OfflinePartition Leader(最常见):当有分区上线时(创建了新分区,或之前的下线分区重新上线)

  2. ReassignPartition Leader:手动执行kafka-reassign-partitions命令时

    ​ 或调用Admin的alterPartitionReassignments方法

  3. PreferredReplicaPartition Leader:手动执行kafka-preferred-replica-election命令时

  4. ControlledShutdownPartition Leader:当Broker正常关闭时

高吞吐/读写数据较快/高性能 的原因:

  1. 顺序写磁盘

  2. 零拷贝:

    数据不用再拷贝到用户态, 即省去了用户态和内核态间的切换

    数据拷贝到页缓存后, 再直接拷贝到Socket缓冲区

    Kafka基础知识(个人总结)

  3. 批处理 节省 网络IO和创建连接 的损耗

  4. 有索引结构 使用稀疏索引存放物理地址

  5. partition分区可以并行读写

优化:

  1. partition数量配置

  2. 日志保留策略设置

  3. 文件刷写到磁盘策略

    # 每当producer写入10000条消息时,刷写数据到磁盘
    log.flush.interval.messages=10000
    # 每1秒钟,刷写数据到磁盘
    log.flush.interval.ms=1000
    
  4. 网络配置

  5. IO操作线程配置

  6. 异步提交(前面的消息提交得不到应答 提交不成功, 不会阻碍后面的消息提交)

  7. compact, 对重复的日志进行清理, 再把小的segment文件合成大的segment文件

  8. 开启压缩

  9. 内存

  10. 消息大小上限设置(默认1MB)

面试题:

  1. Kafka中的ISR、AR又代表什么?
    ISR:与leader保持同步的follower集合
    AR:分区的所有副本

  2. Kafka中的HW、LEO等分别代表什么?
    LEO:每个副本最后一条消息的offset
    HW:一个分区中所有副本最小的HW

  3. 如何提高Kafka里数据的消费速度?

    增加 分区数量 和 消费者数量, 并且使分区数=消费者数, 一个消费者消费一个分区; 避免有些消费者负载过重 或 有些消费者消费不到数据

  4. Kafka中的分区器、序列化器、拦截器之间的处理顺序是什么?
    拦截器 -> 序列化器 -> 分区器

  5. Kafka生产者客户端的整体结构是什么样子的?使用了几个线程来处理?分别是什么?

    Kafka基础知识(个人总结)

  6. "消费组中的消费者个数如果超过topic的分区,那么就会有消费者消费不到数据"这句话是否正确?

    ​ 正确

  7. 消费者提交消费位移时提交的是当前消费到的最新消息的offset还是offset+1?

    ​ offset+1

  8. 有哪些情形会造成重复消费?

    先消费,后提交offset

  9. 那些情景会造成消息漏消费?

    先提交offset,后消费

  10. 当你使用kafka-topics.sh创建(删除)了一个topic之后,Kafka背后会执行什么逻辑?

    1. 会在zookeeper中的/brokers/topics节点下创建一个新的topic节点,如:/brokers/topics/first

    2. 触发Controller的监听程序

    3. Controller 负责topic的创建工作,并更新metadata cache

  11. topic的分区数可不可以增加?如果可以怎么增加?如果不可以,那又是为什么?

    可以增加

    bin/kafka-topics.sh --zookeeper localhost:2181/kafka --alter --topic topic-config --partitions 3
    
  12. topic的分区数可不可以减少?如果可以怎么减少?如果不可以,那又是为什么?

    不可以减少,因为被删除的分区数据难以处理

  13. Kafka有内部的topic吗?如果有是什么?有什么所用?

    有, 是_consumer_offsets,保存消费者offset

  14. Kafka分区分配的概念?

    一个topic多个分区,一个消费者组多个消费者,故需要将分区分配个消费者(roundrobin、range)

  15. 简述Kafka的日志目录结构?

    1. 文件夹的命名规则: topic的名称+分区序号
    2. index和log文件命名规则: 以当前segment的第一条消息的offset命名
  16. 如果我指定了一个offset,Kafka Controller怎么查找到对应的消息?

    索引存的是消息的物理地址, 并不是为每条消息都创建索引, 而是使用稀疏索引
    Kafka基础知识(个人总结)

  17. Kafka Controller的作用?

    1. 负责leader选举, Controller里写的是谁, 谁就是leader

    2. 负责topic的创建工作

    3. 负责更新metadata cache

  18. Kafka中有那些地方需要选举?这些地方的选举策略又有哪些?

    1. ISR
    2. Controller(先到先得)
  19. 失效副本是指什么?有那些应对措施?

    不能及时与leader同步,暂时踢出ISR,等其追上leader之后再重新加入

  20. 为什么Kafka不支持读写分离?

    因为主写从读有 2 个很明 显的缺点:

    1. 数据一致性问题

    2. 延时问题

  21. 为什么复制在Kafka中至关重要?

    复制可以确保消息不会丢失

  22. 为什么需要消息队列, mysql 不能满足需求吗?

    1. mysql没有消息主题 发布/订阅模式, 不能使 只订阅了主题的消费者才能看到相关消息
    
    2. 数据只是日志信息, 没必要使用表结构
    
    3. 消息队列是短期内临时存储,而mysql是长期存储; 实际业务中消息并不需要长期存储,只需要在短期内给消费者提供服务就行了
    
  23. Kafka 对比 Flume

    1. Kafka高吞吐, 且在消息分发方面 有主题发布/订阅 模式, 还有消费者组, 擅于分发消息, 还可以指定Offset进行消费数据
    2. Flume支持丰富多样的 数据采集端和数据输出端
    
  24. 数据传输的事务定义有哪三种?

      1. 至少一次
      2. 至多一次
      3. 精准一次
    
  25. 消费者故障,出现活锁问题如何解决?

    活锁: 消费者能正常向zk发送心跳,但是不 poll 消息

    产生原因: poll的时间间隔过长

    解决: 配置 max.poll.interval.ms 活跃监测机制

    ​ 如果客户端调用 poll 的间隔过大了, 大于配置的最大间隔,就断开当前客户端连接,让其它的消费者 过来消费

  26. 分布式系统中最难解决的两个问题是:

    • 消息的精确一次性投递
    • 消息的顺序性
  27. kafka 分布式(不是单机)的情况下,如何保证消息的顺序性?

    在某些业务场景下,我们需要保证对于有逻辑关联的多条MQ消息被按顺序处理:

    比如对于某一条数据,正常处理顺序是新增-删除,最终结果是数据被删除;

    如果消息没有按序消费,处理顺序可能是删除-新增,最终数据没有被删掉

    解决方式有两种:

    1. 使用单分区

      生产者使用同步提交的方式, 这样分区内是有序的

    2. 使用多分区

      主要需要考虑如下三点:

      1. 生产有序

        生产者只有一个, 且使用同步提交(前面提交的得到应答 提交完成了, 后面的才能继续提交)

      2. 在Kafka中有序

        生产者在写数据的时候,可以指定一个key

        比如在订单 topic 中我们可以指定订单 id 作为 key, 那么相同订单 id 的数据,一定会被分发到同一个 partition中去,而且这个 partition中的数据一定是有顺序的

      3. 消费有序

        然后开启N个线程,每个线程分别消费一个分区的数据即可,这样就能保证顺序性。

  28. kafka 如何不消费重复数据?比如扣款,我们不能重复的去扣

    使用Exactly Once(精准一次性) = At Least Once(acks=-1) + 幂等性

上一篇:RocketMQ学习笔记


下一篇:RocketMQ实战:生产环境中,java开发基础形考作业题