一文入门Kafka,必知必会的概念通通搞定

2,312 阅读12分钟

Kakfa在大数据消息引擎领域,绝对是没有争议的国民老公。

这是kafka系列的第一篇文章。预计共出20篇系列文章,全部原创,从0到1,跟你一起死磕kafka。

本文盘点了 Kafka 的各种术语并且进行解读,术语可能比较枯燥,但真的是精髓中的精髓!

了解Kafka之前我们必须先掌握它的相关概念和术语,这对于后面深入学习 Kafka 各种功能将大有裨益。所以,枯燥你也得给我看完!

大概是有这么些东西要掌握,不多不多,预计20分钟可以吃透:

主题层

主题层有三个儿子,分别叫做:Topic、Partition、Replica。既然我说是三个儿子,那你懂了,是不可分割的整体。

Topic(主题)

Kafka 是分布式的消息引擎系统,它的主要功能是提供一套完备的消息(Message)发布与订阅解决方案。

在 Kafka 中,发布订阅的对象是主题(Topic),你可以为每个业务、每个应用甚至是每类数据都创建专属的主题。

一个Topic是对一组消息的归纳。也可以理解成传统数据库里的表,或者文件系统里的一个目录。

Partition(分区)

一个Topic通常都是由多个partition组成的,创建topic时候可以指定partition数量。

📷 分区优势

为什么需要将Topic分区呢?如果你了解其他分布式系统,你可能听说过分片、分区域等说法,比如 MongoDB 和 Elasticsearch 中的 Sharding、HBase 中的 Region,其实它们都是相同的原理。

试想,如果一个Topic积累了太多的数据以至于单台 Broker 机器都无法容纳了,此时应该怎么办呢?

一个很自然的想法就是,能否把数据分割成多份保存在不同的机器上?这不就是分区的作用吗?其实就是解决伸缩性的问题,每个partition都可以放在独立的服务器上。

当然优势不仅于此,也可以提高吞吐量。kafka只允许单个partition的数据被一个consumer线程消费。因此,在consumer端,consumer并行度完全依赖于被消费的分区数量。综上所述,通常情况下,在一个Kafka集群中,partition的数量越多,意味着可以到达的吞吐量越大。

📷 partition结构

每个partition对应于一个文件夹,该文件夹下存储该partition的数据和索引文件。

如图所示,可以看到两个文件夹,都对应着一个叫做asd的topic,在该台服务器上有两个分区,0和2,那么1呢?在其他服务器上啦!毕竟是分布式分布的!

我们进去asd-0目录中看看是什么?有后缀为.index和.log的文件,他们就是该partition的数据和索引文件:

现在先不管它们是何方神圣,因为我会在【分区机制原理】这篇文章中详细描述。

📷 partition顺序性

现在,我需要你睁大眼睛看看关于分区非常重要的一点:

【每个partition内部保证消息的顺序。但是分区之间是不保证顺序的】

这一点很重要,例如kafka中的消息是某个业务库的数据,mysql binlog是有先后顺序的,10:01分我没有付款,所以pay_date为null,而10:02分我付款了,pay_date被更新了。

但到了kafka那,由于是分布式的,多分区的,可就不一定能保证顺序了,也许10:02分那条先来,这样可就会引发严重生产问题了。因此,一般我们需要按表+主键来分区。保证同一主键的数据发送到同一个分区中。

如果你想要 kafka 中的所有数据都按照时间的先后顺序进行存储,那么可以设置分区数为 1。

Replica (副本)

每个partition可以配置若干个副本。Kafka 定义了两类副本:领导者副本(Leader Replica)和追随者副本(Follower Replica)。只能有 1 个领导者副本和 N-1 个追随者副本。

为啥要用副本?也很好理解,反问下自己为什么重要的文件需要备份多份呢?备份机制(Replication)是实现高可用的一个手段。

需要注意的是:仅Leader Replica对外提供服务,与客户端程序进行交互,生产者总是向领导者副本写消息,而消费者总是从领导者副本读消息。而Follower Replica不能与外界进行交互,它只做一件事:向领导者副本发送请求,请求领导者把最新生产的消息发给它,保持与领导者的同步。

如果对于刚刚所说的主题、分区、副本还有疑惑,那么结合下面这张图再思考一下,我相信你就可以玩转它了:

下图所示,TopicA,具有三个partition,每个partion都有1 个leader副本和 1 个follower者副本。为了保证高可用性,一台机器宕机不会有影响,因此leader副本和follower副本必然分布在不同的机器上。

消息层

Kafka的官方定义是message system,由此我们可以知道Kafka 中最基本的数据单元无疑是消息message,它可理解成数据库里的一条行或者一条记录。消息是由字符数组组成。关于消息你必须知道这几件事:

📷 消息key

发送消息的时候指定 key,这个 key 也是个字符数组。key 用来确定消息写入分区时,进入哪一个分区。你可以用有明确业务含义的字段作为key,比如用户号,这样就可以保证同一个用户号进入同一个分区。

📷 批量写入

为了提高效率, Kafka 以批量batch的方式写入。

一个 batch 就是一组消息的集合, 这一组的数据都会进入同一个 topic 和 partition(这个是根据 producer 的配置来定的) 。

每一个消息都进行一次网络传输会很消耗性能,因此把消息收集到一起再同时处理就高效的多。

当然,这样会引入更高的延迟以及吞吐量:batch 越大,同一时间处理的消息就越多。batch 通常都会进行压缩,这样在传输以及存储的时候效率都更高一些。

📷 位移 生产者向分区写入消息,每条消息在分区中的位置信息由一个叫位移(Offset)的数据来表征。分区位移总是从 0 开始,假设一个生产者向一个空分区写入了 10 条消息,那么这 10 条消息的位移依次是 0、1、2、…、9。

服务端

Kafka 的服务器端由被称为 Broker 的服务进程构成,即一个 Kafka 集群由多个 Broker 组成,Kafka支持水平扩展,broker数量越多,集群吞吐量越高。在集群中每个broker都有一个唯一brokerid,不得重复。Broker 负责接收和处理客户端发送过来的请求,以及对消息进行持久化。

一般会将不同的 Broker 分散运行在不同的机器上,这样如果集群中某一台机器宕机,kafka可以自动选举出其他机器上的 Broker 继续对外提供服务。这其实就是 Kafka 提供高可用的手段之一。

📷 controller

Kafka集群中会有一个或者多个broker,其中有且仅有一个broker会被选举为控制器(Kafka Controller),它负责管理整个集群中所有分区和副本的状态。

当某个分区的leader副本出现故障时,由控制器负责为该分区选举新的leader副本。当检测到某个分区的ISR集合发生变化时,由控制器负责通知所有broker更新其元数据信息。当为某个topic增加分区数量时,同样还是由控制器负责分区的重新分配。

这几句话可能会让你觉得困惑不要方 只是突出下控制器的职能很多,而这些功能的具体细节会在后面的文章中做具体的介绍。

Kafka中的控制器选举的工作依赖于Zookeeper,成功竞选为控制器的broker会在Zookeeper中创建/controller这个临时(EPHEMERAL)节点,此临时节点的内容参考如下:

其中version在目前版本中固定为1,brokerid表示称为控制器的broker的id编号,timestamp表示竞选称为控制器时的时间戳。

两种客户端

Kafka有两种客户端。生产者和消费者。我们把生产者和消费者统称为客户端(Clients)。

向主题Topic发布消息Message的客户端应用程序称为生产者(Producer),生产者程序通常持续不断地向一个或多个主题发送消息。

而订阅这些主题消息的客户端应用程序就被称为消费者(Consumer)。和生产者类似,消费者也能够同时订阅多个主题的消息。

Producer

Producer 用来创建Message。在发布订阅系统中,他们也被叫做 Publisher 发布者或 writer 写作者。

通常情况下,会发布到特定的Topic,并负责决定发布到哪个分区(通常简单的由负载均衡机制随机选择,或者通过key,或者通过特定的分区函数选择分区。) Producer分为Sync Producer 和 Aync Producer。

Sync Producer同步的生产者,即一定要某条消息成功才会发送下一条。所以它是低吞吐率、一般不会出现数据丢失。

Aync Producer异步的生产者,有个队列的概念,是直接发送到队列里面,批量发送。高吞吐率、可能有数据丢失的。

Consumer 和 Consumer Group

📷 消费者

Consumer 读取消息。在发布订阅系统中,也叫做 subscriber 订阅者或者 reader 阅读者。消费者订阅一个或者多个主题,然后按照顺序读取主题中的数据。

📷 消费位移

消费者需要记录消费进度,即消费到了哪个分区的哪个位置上,这是消费者位移(Consumer Offset)。注意,这和上面所说的消息在分区上的位移完全不是一个概念。上面的“位移”表征的是分区内的消息位置,它是不变的,即一旦消息被成功写入到一个分区上,它的位移值就是固定的了。

而消费者位移则不同,它可能是随时变化的,毕竟它是消费者消费进度的指示器嘛。通过存储最后消费的 Offset,消费者应用在重启或者停止之后,还可以继续从之前的位置读取。保存的机制可以是 zookeeper,或者 kafka 自己。

📷 消费者组

ConsumerGroup:消费者组,指的是多个消费者实例组成一个组来消费一组主题,分区只能被消费者组中的其中一个消费者去消费,组员之间不能重复消费。

为什么要引入消费者组呢?主要是为了提升消费者端的吞吐量。多个消费者实例同时消费,加速整个消费端的吞吐量(TPS)。

当然它的作用不仅仅是瓜分订阅主题的数据,加速消费。它们还能彼此协助。假设组内某个实例挂掉了,Kafka 能够自动检测到,然后把这个 Failed 实例之前负责的分区转移给其他活着的消费者,这个过程称之为重平衡(Rebalance)。

你务必先把这个词记住,它是kafka大名鼎鼎的重平衡机制,生产出现的异常问题很多都是由于它导致的。后续我会在【kafka大名鼎鼎又臭名昭著的重平衡】文章中详细分析。

Zookeeper

zookeeper目前在kafka中扮演着举重轻重的角色和作用~是kafka不可缺少的一个组件。

目前,Apache Kafka 使用 Apache ZooKeeper 来存储它的元数据,比如brokers信息、分区的位置和主题的配置等数据就是存储在 ZooKeeper 集群中。

注意我的用词,我只说是目前。why?在 2019 年社区提出了一个计划,以打破这种依赖关系,并将元数据管理引入 Kafka 本身。因为拥有两个系统会导致大量的重复。

在之前的设计中,我们至少需要运行三个额外的 Java 进程,有时甚至更多。事实上,我们经常看到具有与 Kafka 节点一样多的 ZooKeeper 节点的 Kafka 集群!此外,ZooKeeper 中的数据还需要缓存在 Kafka 控制器上,这导致了双重缓存。

更糟糕的是,在外部存储元数据限制了 Kafka 的可伸缩性。当 Kafka 集群启动时,或者一个新的控制器被选中时,控制器必须从 ZooKeeper 加载集群的完整状态。随着元数据数量的增加,加载过程需要的时间也会增加,这限制了 Kafka 可以存储的分区数量。

最后,将元数据存储在外部会增加控制器的内存状态与外部状态不同步的可能性。

因此,未来,Kafka 的元数据将存储在 Kafka 本身中,而不是存储在 ZooKeeper 之类的外部系统中。可以持续关注kafka社区动态哦!

总结

一个典型的kafka集群包含若干个producer(向主题发布新消息),若干consumer(从主题订阅新消息,用Consumer Offset表征消费者消费进度),cousumergroup(多个消费者实例共同组成的一个组,共同消费多个分区),若干broker(服务器端进程)。还有zookeeper。

kafka发布订阅的对象叫主题,每个Topic下可以有多个Partition,Partition中每条消息的位置信息又叫做消息位移(Offset),Partition有副本机制,使得同一条消息能够被拷贝到多个地方以提供数据冗余,副本分为领导者副本和追随者副本。

可以用下面这张图来形象表达kafka的组成:

另外,再po一张思维导图助你回顾本文所述的术语。

重要!!关注【胖滚猪学编程】公众号发送"kafka"。获取本文所有架构图以及Kafka全系列思维导图!

本文来源于公众号:【胖滚猪学编程】。一枚集颜值与才华于一身,不算聪明却足够努力的女程序媛。用漫画形式让编程so easy and interesting!求关注!