《吃透MQ系列》之扒开Kafka的神秘面纱

  • 2021 年 6 月 21 日
  • 筆記

大家好,这是《吃透 MQ 系列》的第二弹,有些珊珊来迟,后台被好几个读者催更了,实属抱歉!

这篇文章拖更了好几周,起初的想法是:围绕每一个具体的消息中间件,不仅要写透,而且要控制好篇幅,写下来发现实在太难了,两者很难兼得。

最后决定还是分成多篇写吧。一方面,能加快下输出频率;另一方面,大家也更容易消化。

废话不多说了,第二弹开始发车。

01 为什么从 Kafka 开始?

《吃透 MQ 》的开篇 围绕 MQ 「一发一存一消费」的本质展开,讲解了 MQ 的通用知识,同时系统性地回答了:如何着手设计一个 MQ?

从这篇文章开始,我会讲解具体的消息中间件, 之所以选择从 Kafka 开始,有 3 点考虑

第一,RocketMQ 和 Kafka 是目前最热门的两种消息中间件,互联网公司应用最为广泛,将作为本系列的重点。

第二,从 MQ 的发展历程来看,Kafka 先于 RocketMQ 诞生,并且阿里团队在实现 RocketMQ 时,充分借鉴了 Kafka 的设计思想。掌握了 Kafka 的设计原理,后面再去理解 RocketMQ 会容易很多。

第三,Kafka 其实是一个轻量级的 MQ,它具备 MQ 最基础的能力,但是在延迟队列、重试机制等高级特性上并未做支持,因此降低了实现复杂度。从 Kafka 入手,有利于大家快速掌握 MQ 最核心的东西。

交代完背景,下面请大家跟着我的思路,一起由浅入深地分析下 Kafka。

02 扒开 Kafka 的面纱

在深入分析一门技术之前,不建议上来就去了解架构以及技术细节,而是先弄清楚它是什么?它是为了解决什么问题而产生的?

掌握这些背景知识后,有利于我们理解它背后的设计考虑以及设计思想。

在写这篇文章时,我查阅了很多资料,关于 Kafka 的定义可以说五花八门,不仔细推敲很容易懵圈,我觉得有必要带大家捋一捋。

我们先看看 Kafka 官网给自己下的定义:

Apache Kafka is an open-source distributed event streaming platform.

翻译成中文就是:Apache Kafka 是一个开源的分布式流处理平台。

Kafka 不是一个消息系统吗?为什么被称为分布式的流处理平台呢?这两者是一回事吗?

一定有读者会有这样的疑问,要解释这个问题,需要先从 Kafka 的诞生背景说起。

Kafka 最开始其实是 Linkedin 内部孵化的项目,在设计之初是被当做「数据管道」,用于处理以下两种场景:

1、运营活动场景:记录用户的浏览、搜索、点击、活跃度等行为。

2、系统运维场景:监控服务器的 CPU、内存、请求耗时等性能指标。

可以看到这两种数据都属于日志范畴,特点是:数据实时生产,而且数据量很大。

Linkedin 最初也尝试过用 ActiveMQ 来解决数据传输问题,但是性能无法满足要求,然后才决定自研 Kafka。

所以从一开始,Kafka 就是为实时日志流而生的。了解了这个背景,就不难理解 Kafka 与流数据的关系了,以及 Kafka 为什么在大数据领域有如此广泛的应用?也是因为它最初就是为解决大数据的管道问题而诞生的。

接着再解释下:为什么 Kafka 被官方定义成流处理平台呢?它不就提供了一个数据通道能力吗,怎么还和平台扯上关系了?

这是因为 Kafka 从 0.8 版本开始,就已经在提供一些和数据处理有关的组件了,比如:

1、Kafka Streams:一个轻量化的流计算库,性质类似于 Spark、Flink。

2、Kafka Connect:一个数据同步工具,能将 Kafka 中的数据导入到关系数据库、Hadoop、搜索引擎中。

可见 Kafka 的野心不仅仅是一个消息系统,它早就在往「实时流处理平台」方向发展了。

这时候,再回来看 Kafka 的官网介绍提到的 3 种能力,也不难理解了:

1、数据的发布和订阅能力(消息队列)

2、数据的分布式存储能力(存储系统)

3、数据的实时处理能力(流处理引擎)

这样,kafka 的发展历史和定义基本缕清了。当然,这个系列仅仅关注 Kafka 的前两种能力,因为这两种能力都和 MQ 强相关。

03 从 Kafka的消息模型说起

理解了 Kafka 的定位以及它的诞生背景,接着我们分析下 Kafka 的设计思想。

上篇文章中我提到过:要吃透一个MQ,建议从 「消息模型」 这种最核心的理论层面入手,而不是一上来就去看技术架构,更不要直接进入技术细节。

所谓消息模型,可以理解成一种逻辑结构,它是技术架构再往上的一层抽象,往往隐含了最核心的设计思想。

下面我们尝试分析下 Kafka 的消息模型,看看它究竟是如何演化来的?

首先,为了将一份消息数据分发给多个消费者,并且每个消费者都能收到全量的消息,很自然的想到了广播。

紧接着问题出现了:来一条消息,就广播给所有消费者,但并非每个消费者都想要全部的消息,比如消费者 A 只想要消息1、2、3,消费者 B 只想要消息4、5、6,这时候该怎么办呢?

这个问题的关键点在于:MQ 不理解消息的语义,它根本无法做到对消息进行分类投递。

此时,MQ 想到了一个很聪明的办法:它将难题直接抛给了生产者,要求生产者在发送消息时,对消息进行逻辑上的分类,因此就演进出了我们熟知的 Topic 以及发布-订阅模型。

这样,消费者只需要订阅自己感兴趣的 Topic,然后从 Topic 中获取消息即可。

但是这样做了之后,仍然存在一个问题:假如 多个消费者都对同一个 Topic 感兴趣(如下图中的消费者 C),那又该如何解决呢?

如果采用传统的队列模式(单播),那当一个消费者从队列中取走消息后,这条消息就会被删除,另外一个消费者就拿不到了。

这个时候,很自然又想到下面的解决方案:

也就是:当 Topic 每增加一个新的消费者,就「复制」一个完全一样的数据队列。

这样问题是解决了,但是随着下游消费者数量变多,将引发 MQ 性能的快速退化。尤其对于 Kafka 来说,它在诞生之初就是处理大数据场景的,这种复制操作显然成本太高了。

这时候,就有了 Kafka 最画龙点睛的一个解法:它将所有消息进行了持久化存储,由消费者自己各取所需,想取哪个消息,想什么时候取都行,只需要传递一个消息的 offset 即可。

这样一个根本性改变,彻底将复杂的消费问题又转嫁给消费者了,这样使得 Kafka 本身的复杂度大大降低,从而为它的高性能和高扩展打下了良好的基础。(这是 Kafka 不同于 ActiveMQ 和 RabbitMQ 最核心的地方)

最后,简化一下,就是下面这张图:

这就是 Kafka 最原始的消息模型。

这也间接解释了第二章节中:为什么官方会将 Kakfa 同时定义成存储系统的原因。

当然 Kafka 的精妙设计远非这些,由于篇幅原因,后面的文章再接着分析。

04 写在最后

这篇文章从 Kafka 的诞生背景讲起,带大家捋清了 Kafka 的定义和它要解决的问题。

另外,一步步分析了 Kafka 的消息模型和设计思想,这是 Kafka 最顶层的抽象。

希望大家有所收获,下篇文章将会深入分析 Kafka 的架构设计,我们下期见!

– End –

作者简介:985硕士,前亚马逊工程师,现58转转技术总监

欢迎扫描下方的二维码,关注我的个人公众号:武哥漫谈IT,精彩原创不断!