Kafka 学习与实践

Kafka 学习与实践

Kafka-Banner

一个分布式的可分区、可复制的提交日志服务, 一个高性能、高可用的消息队列
– Kafka Document

一、话题、代理与分区

1. 话题 – Topic

Kafka 的话题类似其他传统消息的队列的话题或者channel, 是消息的路由以及消息内容的存储载体根本。

2. 代理 --broker

一台kafka服务器就称之为Broker, kafka的Broker分为两类:

普通Broker

  普通Broker主要负责生产者消息的写入监听和消费者消息的请求监听, 以及对消息进行分区路由和对消费者进行分区分配等工作

控制者Broker

  控制者Broker具有除普通Broker节点所以功能之外, 还需要负责分区的首领选取。一个Broker集群中第一个启动并且成在Zookeeper创建了成功一个临时节点 /controller 的broker将成为集群的控制者broker, 其他 broker 在启动时也会尝试创建这个节点,不过它们会收到一个“节点已存在”的异常,然后“意识”到控制器节点已存在,也就是说集群里已经有一个控制器, 然后它们就会转而在Zookeeper创建一个针对当前controller的watch对象,用于监听控制器节点的状态, 一旦控制器被关闭或者与zk断开连接,那么将进行新的一轮控制权抢夺, 重新创建/controller临时节点和基于新控制器的监听对象。

  每次创建成功/controller 会伴生创建一个 controller epoch 纪元数字, 之后的控制器创建会生成更大的epoch, zookeeper 和 普通broker只认可最大的epoch, 控制器会持续监听普通Broker, 如果有broker离开或加入集群,就会发起分区首领再选举

  简而言之, Kafka 使用 Zookeeper 的临时节点来选举控制器, 并在节点加入集群或退出集群时通知控制器。控制器负责在节点加入或离开集群时进行分区首领选举。控制器使用epoch 来避免“脑裂” 。“脑裂”是指两个节点同时认为自己是 当前的控制器。

3. 分区 – partition

顺序存储

  为了方便扩展, 当一个Topic 的消息量很大的时候会选择将Topic的消息分布到多个Broker(服务器)上, Kafka通过Topic的分区机制实现跨物理机存储:

  • 一个Topic可以拥有多个Partition, Partition 可以跨多个broker分布, 而无需都集中在一个broker上
  • partition中的每条消息都会被分配一个有序的id(offset)。 kafka只保证按一个partition中的顺序但不连续的将消息发给consumer , 不保证一个topic的整体(多个partition间)的顺序,如果需要保证全局有序则需要设定为一个Topic中只有一个Partition
  • 每个Partition在整个topic 内隔离独立, 两个Partition之间互不影响

复制同步

  集群情况下, 一个Topic会将不同的Partition分配到不同的Broker机器上做横向物理拓展, 为了保证可用性和持久性, Kafka 使用主题来组织数据,通过--partitions指定多个Partition分区, 通过--replication-factor复制因子来决定每个分区拥有多少个副本。 副本被保存在不同的Broker上, 副本分为以下两类:

  • 首领副本:每个分区会分配一个首领副本, 由控制者broker进行选举。为了保证数据一致性, 所有针对该分区的生产者和消费者都会经过这个副本。首领副本需要将消息复制给所有副本, 同时需要确认哪些跟随者副本的状态和自己是一致的:上

  • 跟随副本:首领副本以外的副本都是跟随副本, 跟随副本处理来自客户端的请求, 它们唯一的任务就是从首领那里复制消息,保持与首领一致的状态。如果首领发生崩渍,其中的一个跟随者会被提升为新首领。

    • 同步副本:与首领副本保持状态一直的分区副本
    • 不同步副本:由于网络原因或者宕机等原因导致与首领状态不一致(滞后消息)的分区副本

    如果一个副本10s内没有请求任何消息, 也就是说一个副本的消息滞后了10s,该副本就会被认为是不同步副本的, 对应的,持续获取消息不断的副本被认为是同步副本, 默认情况下, 只有同步副本才能成为新的副本首领, 当然也有不完全选举的情况。

二、Kafka 生产者

  Kafka 的生产者负责将消息发送到broker, 一次消息的生产从创建一个ProducerRecord对象开始, ProducerRecord对象需要包含目标主题和要发送的内容, 还可以指定分区或者键(不能同时指定, 涉及到消息分区分配), 在发送ProducerRecord 对象时,生产者需要将键和值对象序列化成字节数组, 这样才能在网络上传输。

Kafka生产者组件图

图: Kafka 生产者组件

1. 序列化器

  Kafka 默认数据传输格式为字节数组, 服务器在接受和发送消息的时候,都需要接受和发送字节数组格式的数据, 但是可以通过指定序列化器, 来让客户端支持 语言基础数据类型格式的消息发送与接受:kafka自定义消息序列化和反序列化方式Apache Avro

2. 分区器

  当消息序列化后,将被发送给到分区器, 如果ProducerRecord对象指定了分区,就按照指定的分区发送, 如果没有指定, 将按照分区键进行分配。

分区键机制

  生产者在生产数据的时候,可以为每条消息指定Key,这样消息被发送到broker时,会根据分区规则选择被存储到哪一个分区中,如果分区规则设置的合理,那么所有的消息将会被均匀的分布到不同的分区中,这样就实现了负载均衡和水平扩展。需要显示地配置生产者端的参数partitioner.class可以自定义分区键规则, 需要实现

org.apache.kafka.clients.producer.Partitioner类实现自定义规则

  • 如果键被设置为null, 并且使用了默认的分区器, 那么记录将被分区器使用轮询算法将消息均匀分布到个个分区上
  • 如果键不为null, 并使用了默认的分区器, 那么Kafka会对键进行散列(使用的是Kafka自己的散列算法, 散列值不受到Java版本影响) ,然后根据散列值把消息映射到特定的分区上。 需要注意的是基于散列算法的原理, 同一个分区键总是会被映射到同一个分区值上, 这也就意味着, 不可用的分区也会被使用到,并且会产生对应的错误, 所以如果要使用键来映射, 那么最好在创建主题的时候将分区规划好, 而且永远不要添加新的分区以防散列不到新分区上去

  在确定好分区后, 消息就会被加入到指定的记录批次里, 同一消息批次发送的目标主题和分区一致。服务器成功接收到消息后会将消息 元数据返回。

3. 消息发送方式

同步发送

  消息同步发送, broker的消息发送线程在收到响应之前, 不会做其他的事情, 会保持阻塞状态

异步发送

  消息发送过后,broker不会等待消息的响应,可以继续发送之后的消息, 消息发送的结果通过回调的方式通知

4. 生产者配置

ACK配置

  • ack = 0: 非阻塞模式, producer不等待broker 同步消息完成的确认, 直接发送下一条(批次)的消息, 低延迟性, 会一直无阻塞的发送消息,但是同时具有低可靠性和持久性缺点, 当目标broker宕机时,producer会不知情依旧发送消息,导致消息丢失的可能性
  • ack = 1: producer 只需要确认目标分区首领接受到消息即可,在收到消息接受确认的之后,就会发送下一条/批次消息, 具有一定的持久性和可靠性,但是当一条消息在发送给某一个首领分区后,在首领分区进行复制的前,该首领分区的broker 宕机了, 其他副本分区没有收到消息,在rebalance 之后不同步副本分区成为新的首领分区, 就会导致数据不一致和数据丢失
  • ack = all/-1 : 当首领分区收到消息且所有可用(ISR)副本分区都复制到消息后, 返回ack响应,然后producer 才能发送下一条/批消息, 持久性和可靠性最好,但是相对延迟较高

buffer.memory

  该参数用来设置生产者内存缓冲区的大小,生产者用它缓冲要发送到服务器的消息, 如果应用程序发送消息的速度超过发送到服务器的速度,会导致生产者空间不足。

compress.type

  默认情况下,消息发送时不会被压缩。该参数可以设置为 snappy 、 gzi.p 或 lz4 ,它指定了消息被发送给 broker 之前使用哪一种压缩算也进行压缩。 snappy 压缩算怯由 Googl巳发明,它占用较少的 CPU ,却能提供较好的性能和相当可观的压缩比,如果比较关注性能和网络带宽,可以使用这种算告 。 gzip 压缩算蓓一般会占用较多的 CPU,但会提供更高的压缩比,所以如果网络带宽比较有限,可以使用这种算法。使用压缩可以降低网络传输开销和存储开销,而这往往是向 Kafka 发送消息的瓶颈所在。

batch.size

  当有多个消息需要被发送到同一个分区时,生产者会把它们放在罔一个批次里。该参数指定了一个批次可以使用的内存大小,按照字节数计算(而不是消息个数), 批次被填满,批次里的所有消息会被发送出去。不过生产者井不一定都会等到批次被填满才发送, 具体发送间隔可以根据 linger.ms来配置

三、Kafka 消费者

1. 消费者和消费者群组

kafka 的消费者负责从订阅的topic中读取消息进行消费, 消费者从属于消费者群组(Consumer Group: CG)。

一般一个群组中的消费者订阅的是同一个主题, 每个消费者负责接收主题中一部分分区的消息

实际上, 经过测试,得出一个CG中的comsumer可以订阅不同的topic,相互之间不会影响消费,可以认为此时一个CG划分为了多个小的CG, 但是不建议这么做,容易混淆订阅关系

测试图如下:

1CG*N TP

一般地,如果一个Consumer拥有多个分区, 订阅的消费者会有以下情况:

  • 多个分区对应一个消费者

多个分区一个消费者

  这种情况下,所有分区被同一个消费者拥有, 消费者能够保证从topic有序消费(自己测试, 不敢笃定)

有序消费
  • 多个消费者对应多个分区,但是分区数量大于消费者数

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JFSKavb5-1578971190441)(http://img.tonyiscoding.top/kafka/2C4P.png)]

  此时分区会按照分区分配策略依次分配给对应群组的所有消费者

  • 多个消费者对应多个分区,但是分区数量小于消费者

消费者数量大于分区数量

   建议是group中的consumer数量不要超过订阅的topic含有的partition数, 因为分配过后会导致部分消费者无法分配到分区的情况, 将无法获取到消息, 白白浪费资源

kafka 能给出的保证是: 一个分区的特定一条消息只能被统一CG中的某一个消费者消费到

  • 多个消费者群组

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U62WJmGD-1578971190447)(http://img.tonyiscoding.top/kafka/2G.png)]

  Kafka多个消费者群组之间消费独立,分区分配独立,负载独立

2. 消费唯一性

kafka 可以保证某个topic内的指定某一条消息, 只投送给某个订阅的CG中的一个consumer, 原理是topic 会对分区和消费者进行分配,且会保证一个partition最多对应一个consumer, 就能由此保证消费唯一性

3. 多播和单播
kafka消息的单播和多播都是基于topic 和 group 来说的, 私以为是不太符合传统意义上的单播和多播概念

多播: 一个topic 对应多个消费者组就能实现多播,如果要实现绝对多播,那么就需要配置为每个消费者组内只含有一个consumer

单播: 消息只发送给单个group的单个消费者即可

4. 消息有序性

  kafka 能保证消息在单个partition 内有序, 但是不连续, 如果需要保证全局有序,则需要保证一个topic内只含有一个partition

5. 消息轮询

  消息轮询是消费者的核心, 通过单独一个线程做简单的轮询向服务器请求数据, 一旦消费者订阅了主题, 轮询就会处理所有的细节问题, 包括群组协调、发送心跳和获取数据

6. 消费者配置

fetch.min.byte

  消费者从服务器获取记录的最小字节数, broker在收到消息获取请求时, 如果消息数量小于这个设定, 将不会返回而是等到有足够的消息时才统一返回给消费者

fetch.max.wait.ms

  指定Broker的等待时间, broker在收到消费者消息请求的时候,如果不满足最小请求字节数, 需要等待设定的MS时间再做超时返回, 将已有的消息全部返回

max.partition.fetch.bytes

  服务器从每个分区里返回给消费者的最大字节数, 他的默认值是1MB

partition.assignment.strategy

  分区会被分配给一个群组的消费者, PartitionAssignor根据给定的消费者和主题,决定哪些分区应该被分配给哪个消费者, kafka有两个默认的分配策略:

  假设现在有一个Topic – T0, T0拥有四个分区P0,P1,P2,P3 分配给拥有两个消费者的消费者群组,消费者分别为C0,C1

  • Range(顺序分配)

    顺序分配会把主题的若干个连续的分区分配给消费者, 那么分配的结果为 C0 拥有P0,P1的所有权,C1拥有P2,P3的所有权, 这种情况下,当分区数能被消费者群的消费者整除的时候, 分配不会有问题, 否则则会产生最大为(消费者数量-1个偏差)

  • RoundRobin(轮询分配)

    轮询分配是依次遍历消费者群组,每轮按顺序分配一个分区,那么分配的结果为 C0拥有P0,P2的所有权, C1拥有P1,P3的所有权,这种裙裤下,分配最大的误差能控制在1个以内

7. 分区再均衡(rebalance)机制

  分区的消费所有权从一个消费者转移到另外一个消费者身上,这样的行为被称之为再均衡。所谓再均衡就是将分区与消费者之间的绑定关系进行一次重新的绑定,不过会尽量保证少的变动发生。在kafka的设计中,以下情况会触发分区的再均衡机制:

  • 当有消费者加入或者退出了消费者群组(一般是人为添加或删除、服务宕机等)
  • 当话题分区发生变化时(比如为topic添加新的分区)

kafka的分区再均衡都是由控制器Broker来做处理的,包括分区分配和副本首领选举

再均衡机制保证了Kafka的高可用和伸缩性, 但是在再均衡期间 消费者无法读取消息,会造成整个群组的一小段时间内的不可用,。此外,当一个分区被分配给一个新的消费者时, 所以应该尽量避免再均衡的发生,只是作为一种保障手段。

8. 消费提交和偏移量

什么是提交?

  在kafka中, (同一消费者群组内)每次消费者请求消息记录, 返回的都是由生产者写 入Kafka还没被消费者读取过 的记录, Kafka不需要得到消费者的消息消费确认,而是通过追踪消息在分区的位置, 来 确认消息是否被消费, 并且被 哪个消费者消费掉。

  通常将更新分区当前消费位置的操作叫作提交

如何提交?

  Kafka 的消费者通过向一个名为_consumer_offset的特殊主题发送消息,消息包含对每个分区的消费偏移量。 如果所有消费者永远常规运行, 那么偏移量没有任何作用; 但是如果触发了再均衡,那么分区的所有权会发生变 动, 为了能够继续消费者的工作,这个时候消费者需要读取每个分区的最后一个消息的偏移量, 然后从偏移量的指定地 方继续工作:

偏移量保存主题

消费异常产生

  1. 如果提交的偏移量小于客户端最后一次消息的偏移量(也就是说, 消费者客户端没有将最新的消费便宜递交), 此 时如果发生再均衡, 会导致消息消费重复:

重复消费

图:重复消费

  2. 如果提交的偏移量大于客户端最后一次消息的偏移量(这种情况目前还无法想到), 此时发生再均衡, 会导致 在两个偏移量之间的消息丢失:
消息丢失

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值