前言
本文主要讲解了Kafka的消费模型,以及学习Kafka必须要懂的基础概念,通过学习和整理,将入门的部分用我认为更容易理解的方式整合到一起,希望对你有所帮助
简介
Kafka 是一个分布式、支持分区的(partition)、多副本的(replica),基于 Zookeeper 协调的分布式消息系统。Kafka 基于 Scala 和 Java 开发,相比较于其他消息队列,其设计中大量使用了批量处理和异步的思想,最高可以每秒处理千万级别的消息
Kafka 与周边生态系统的兼容性是最好的没有之一,尤其在大数据和流计算领域
Kafka 具有三个关键功能:
- 消息队列:发布和订阅消息流,这个功能类似于消息队列,这也是 Kafka 也被归类为消息队列的原因。
- 容错的持久方式存储记录消息流: Kafka 会把消息持久化到磁盘,有效避免了消息丢失的风险·。
- 流式处理平台: 在消息发布的时候进行处理,Kafka 提供了一个完整的流式处理类库。
Kafka 主要有两大应用场景:
- 消息队列 :建立实时流数据管道,以可靠地在系统或应用 程序之间获取数据
- 数据处理: 构建实时的流数据处理程序来转换或处理数据流
Kafka 的消费模型
Kafka 采用了 发布 - 订阅模型
发布订阅模型(Pub-Sub) 使用主题(Topic) 作为消息通信载体,类似于广播模式;发布者发布一条消息,该消息通过主题传递给所有的订阅者,在一条消息广播之后才订阅的用户则是收不到该条消息的
顺便解释一下上面出现的几个名词的概念:
Producer(生产者) :消息的生产方
Consumer(消费者) :消息的消费方
Broker(代理) :对于 Broker 可以简单理解为一个独立的 Kafka 实例, 通过分布式架构,Kafka 集群可以横向扩容很多的 Broker,以增加自己的并发处理能力
Topic(主题) : Producer 将消息发送到特定的主题,Consumer 通过订阅特定的 Topic(主题) 来消费消息
Partition(分区) :Partition 属于 Topic 的一部分。一个 Topic 可以有多个 Partition ,并且同一 Topic 下的 Partition 可以分布在不同的 Broker 上,这也就表明一个 Topic 可以横跨多个 Broker,但是 Partition 不会跨越 Broker
GroupId(消费组 ID) :每个消费者都会有一个 GroupId,GroupId 相等的话代表他们属于同一个消费组
Offset(消息在 Partition 的偏移量) :消息被追加到 Partition 时都会被分配一个 Offset,Partition 会维护每个 GroupId 的 Offset,也就是记录不同消费组消费的位置,尽管一个消费组有多个消费者,但它们只能按顺序的消费这个 Partition 中的消息,而不会重复消费
从不同角度解释Kafka的消费机制
根据Kafka的消息模型图,从不同的角度来描述一下 Kafka 的消费机制
对于 Topic 来说:
- 消费者在给 Topic 发送消息时:
- 如果在发消息的时候指定了分区,则消息投递到指定的分区
- 如果既没有指定分区,且消息的 Key 也是空,则用轮询的方式选择一个分区
- 如果没有指定分区,但是消息的 Key 不为空,则基于 Key 的哈希值来选择一个分区
- Topic 可以跨 Broker,但是 Partition 不会
- Topic不保证不同Partition消息的顺序,只能保证同一个Partition中的消息是有序的
- Topic会给所有的订阅组发送各个Partition中的消息,并且Partition会维护各个消费组的Offset以确保不会重复消费
对于Consumer来说:
- 每个Consumer都有一个GroupId(消费组ID),一个消费组可以有多个Consumer
- Consumer可以订阅多个Topic,但是对于Topic发送的消息,同一个消费组不会重复的收到Topic的消息。也就是说,哪怕一个消费组的多个Consumer都订阅了某一Topic,但是Topic过来的消息只会给其中一个Consumer,其它订阅该Topic的Consumer不会再重复收到
如何保证消费顺序
保证消费顺序最简单的办法,就是将所有数据指定一个Partition,只管往同一个Partition塞,这样消费者收到的消息是顺序的
另一种办法,是通过发送消息时,指定参数Key和Partition来实现,如果指定了Key,那么相同Key的消息会被发往同一个Partition,这样能保证不同Key的消息是有序的
有一种情况,会导致消息的有序性遭到破坏:
假设现在Producer发送消息时,因为网络波动发送失败,进入重试逻辑,此时有另一条本该再它之后的消息被先发到Partition上,然后消息重试成功了,此时重试的消息的顺序就被打乱了
面对严格要求消息有序的情况,只能是把重试次数设为0,然后处理发送失败的情况,如果有其他比较好的方法,欢迎再评论区告诉我
多副本机制(Replica)
为了提高消息存储的安全性和Kafka的容灾能力,引入了多副本机制
多副本机制是针对Partition设计的,可以这么理解,Kafka为Partition拷贝了多份,并且这个份数是可以指定的,多份Partition中有一个Leader,其他叫Follower,当Leader不行了,挂了,会从所有Follower中挑一个合格的上位,与Leader同步程度达不到要求的都是不够资格的小弟,不参加Leader选举
我们平时其实只跟Leader打交道,发送的消息都在Leader中,消息提交到Leader后其他Follower会自动从Leader中拉取同步
Zookeeper对Kafka的作用
1. Broker注册
每个 Broker 在启动时,都会到 Zookeeper 上的 /brokers/ids 进行注册,创建属于自己的节点。每个 Broker 就会将自己的 IP 地址和端口等信息记录到该节点中去,如下图:
2. Topic注册
在 Kafka 中,同一个Topic 的消息会被分成多个分区并将其分布在多个 Broker 上,这些分区信息及与 Broker 的对应关系也都是由 Zookeeper 在维护
3. 负载均衡
对于Topic来说,可以指定多个Partition,而多个Partition又可以分不到不同的Broker上
当生产者生产消息后会尽量投递到不同 Broker的Partition中,当Consumer消费的时候,Zookeeper 可以根据当前的 Partition 数量以及 Consumer 数量来实现动态负载均衡
Reference
CSDN:面试官问:kafka中生产者是如何把消息投递到哪个分区的?消费者又是怎么选择分区的?..._程序员小乐-CSDN博客