Kafka 入门
此专栏主要基于 Kafka 2.3 版本。
参考 Kafka 核心技术与实战 (opens new window) 第 01-05 讲
# 1. 消息引擎系统 ABC
Kafka 是什么呢?用一句话概括一下:Apache Kafka 是一款开源的消息引擎系统。很多场景下也称他为“消息队列”、“消息中间件”。
像 Kafka 这一类的系统国外有专属的名字叫 Messaging System,国内很多文献将其简单翻译成消息系统。我个人认为并不是很恰当,因为它片面强调了消息主体的作用,而忽视了这类系统引以为豪的消息传递属性,就像引擎一样,具备某种能量转换传输的能力,所以我觉得翻译成消息引擎反倒更加贴切。
既然消息引擎是用于在不同系统之间传输消息的,那么如何设计待传输消息的格式从来都是一等一的大事。Kafka 选择使用纯二进制的字节序列。当然消息还是结构化的,只是在使用之前都要将其转换成二进制的字节序列。
消息设计出来之后还不够,消息引擎系统还要设定具体的传输协议,即我用什么方法把消息传输出去。常见的有两种方法:
- 点对点模型:也叫消息队列模型。如果拿上面那个“民间版”的定义来说,那么系统 A 发送的消息只能被系统 B 接收,其他任何系统都不能读取 A 发送的消息。日常生活的例子比如电话客服就属于这种模型:同一个客户呼入电话只能被一位客服人员处理,第二个客服人员不能为该客户服务。
- 发布 / 订阅模型:与上面不同的是,它有一个主题(Topic)的概念,你可以理解成逻辑语义相近的消息容器。该模型也有发送方和接收方,只不过提法不同。发送方也称为发布者(Publisher),接收方称为订阅者(Subscriber)。和点对点模型不同的是,这个模型可能存在多个发布者向相同的主题发送消息,而订阅者也可能存在多个,它们都能接收到相同主题的消息。生活中的报纸订阅就是一种典型的发布 / 订阅模型。
Kafka 同时支持这两种消息引擎模型,后面会详细讲解。
JMS
提到消息引擎系统,你可能会问 JMS 和它是什么关系。JMS 是 Java Message Service,它也是支持上面这两种消息引擎模型的。严格来说它并非传输协议而仅仅是一组 API 罢了。不过可能是 JMS 太有名气以至于很多主流消息引擎系统都支持 JMS 规范,比如 ActiveMQ、RabbitMQ、IBM 的 WebSphere MQ 和 Apache Kafka。当然 Kafka 并未完全遵照 JMS 规范,相反,它另辟蹊径,探索出了一条特有的道路。
我们为什么要使用消息引擎系统呢?比如系统 A 不能直接发送消息给系统 B,中间还要隔一个消息引擎呢?
答案就是“削峰填谷”。所谓的“削峰填谷”就是指缓冲上下游瞬时突发流量,使其更平滑。特别是对于那种发送能力很强的上游系统,如果没有消息引擎的保护,“脆弱”的下游系统可能会直接被压垮导致全链路服务“雪崩”。但是,一旦有了消息引擎,它能够有效地对抗上游的流量冲击,真正做到将上游的“峰”填满到“谷”中,避免了流量的震荡。消息引擎系统的另一大好处在于发送方和接收方的松耦合,这也在一定程度上简化了应用的开发,减少了系统间不必要的交互。
作者:
在今天结束之前,我还想和你分享一个自己的小故事。在 2015 年那会儿,我花了将近 1 年的时间阅读 Kafka 源代码,期间多次想要放弃。你要知道阅读将近 50 万行源码是多么痛的领悟。我还记得当初为了手写源代码注释,自己写满了一个厚厚的笔记本。不过幸运的是我坚持了下来,之前的所有努力也没有白费,以至于后面写书、写极客时间专栏就变成了一件件水到渠成的事情。 最后我想送给你一句话:聪明人也要下死功夫。我不记得这是曾国藩说的还是季羡林说的,但这句话对我有很大影响,当我感到浮躁的时候它能帮我静下心来踏踏实实做事情。希望这句话对你也有所启发。切记:聪明人要下死功夫!
# 2. Kafka 的术语
# 2.1 主题、生产者与消费者、客户端与 Broker
Kafka 属于分布式的消息引擎系统,它的主要功能是提供一套完备的消息发布与订阅解决方案。在 Kafka 中,发布订阅的对象是主题(Topic),你可以为每个业务、每个应用甚至是每类数据都创建专属的主题。
向主题发布消息的客户端应用程序称为生产者(Producer),生产者程序通常持续不断地向一个或多个主题发送消息,而订阅这些主题消息的客户端应用程序就被称为消费者(Consumer)。和生产者类似,消费者也能够同时订阅多个主题的消息。我们把生产者和消费者统称为客户端(Clients)。你可以同时运行多个生产者和消费者实例,这些实例会不断地向 Kafka 集群中的多个主题生产和消费消息。
Kafka 的服务器端由被称为 Broker 的服务进程构成,即一个 Kafka 集群由多个 Broker 组成,Broker 负责接收和处理客户端发送过来的请求,以及对消息进行持久化。虽然多个 Broker 进程能够运行在同一台机器上,但更常见的做法是将不同的 Broker 分散运行在不同的机器上,这样如果集群中某一台机器宕机,即使在它上面运行的所有 Broker 进程都挂掉了,其他机器上的 Broker 也依然能够对外提供服务。这其实就是 Kafka 提供高可用的手段之一。
# 2.2 主从备份
实现高可用的另一个手段就是主从备份机制(Replication)。通过数据复制在多台机器上得到多个副本(Replica)。这些副本保存着相同的数据,但却有不同的角色和作用。Kafka 定义了两类副本:
- 领导者副本(Leader Replica):对外提供服务,可以与客户端程序进行交互。
- 追随者副本(Follower Replica):被动地追随领导者副本,不与外界交互。
# 2.3 分区
Kafka 通过分区(Partitioning)来将积累的数据分割到不同的 Broker 中。Kafka 中的分区机制指的是将每个主题划分成多个分区(Partition),每个分区是一组有序的消息日志。生产者生产的每条消息只会被发送到一个分区中,也就是说如果向一个双分区的主题发送一条消息,这条消息要么在分区 0 中,要么在分区 1 中。如你所见,Kafka 的分区编号是从 0 开始的,如果 Topic 有 100 个分区,那么它们的分区号就是从 0 到 99。
讲到这里,你可能有这样的疑问:刚才提到的副本如何与这里的分区联系在一起呢?实际上,副本是在分区这个层级定义的。每个分区下可以配置若干个副本,其中只能有 1 个领导者副本和 N-1 个追随者副本。生产者向分区写入消息,每条消息在分区中的位置信息由一个叫位移(Offset)的数据来表征。分区位移总是从 0 开始,假设一个生产者向一个空分区写入了 10 条消息,那么这 10 条消息的位移依次是 0、1、2、…、9。
至此我们能够完整地串联起 Kafka 的三层消息架构:
- 第一层是主题层,每个主题可以配置 M 个分区,而每个分区又可以配置 N 个副本。
- 第二层是分区层,每个分区的 N 个副本中只能有一个充当领导者角色,对外提供服务;其他 N-1 个副本是追随者副本,只是提供数据冗余之用。
- 第三层是消息层,分区中包含若干条消息,每条消息的位移从 0 开始,依次递增。
- 最后,客户端程序只能与分区的领导者副本进行交互。
# 2.4 Broker 如何持久化数据
Kafka 使用消息日志(Log)来保存数据,一个日志就是磁盘上一个只能追加写(Append-only)消息的物理文件。因为只能追加写入,故避免了缓慢的随机 I/O 操作,改为性能较好的顺序 I/O 写操作,这也是实现 Kafka 高吞吐量特性的一个重要手段。
不过如果你不停地向一个日志写入消息,最终也会耗尽所有的磁盘空间,因此 Kafka 必然要定期地删除消息以回收磁盘。怎么删除呢?简单来说就是通过日志段(Log Segment)机制。在 Kafka 底层,一个日志又近一步细分成多个日志段,消息被追加写到当前最新的日志段中,当写满了一个日志段后,Kafka 会自动切分出一个新的日志段,并将老的日志段封存起来。Kafka 在后台还有定时任务会定期地检查老的日志段是否能够被删除,从而实现回收磁盘空间的目的。
# 2.5 消费者
这里再重点说说消费者。
之前我们提到了两种消息模型:点对点模型(Peer to Peer,P2P)和发布订阅模型。这里面的点对点指的是同一条消息只能被下游的一个消费者消费,其他消费者则不能染指。在 Kafka 中实现这种 P2P 模型的方法就是引入了消费者组(Consumer Group)。所谓的消费者组,指的是多个消费者实例共同组成一个组来消费一组主题。这组主题中的每个分区都只会被组内的一个消费者实例消费,其他消费者实例不能消费它。
为什么要引入消费者组呢?主要是为了提升消费者端的吞吐量。多个消费者实例同时消费,加速整个消费端的吞吐量(TPS)。我会在专栏的后面详细介绍消费者组机制,所以现在你只需要了解消费者组是做什么的即可。另外这里的消费者实例可以是运行消费者应用的进程,也可以是一个线程,它们都称为一个消费者实例(Consumer Instance)。
消费者组里面的所有消费者实例不仅“瓜分”订阅主题的数据,而且更酷的是它们还能彼此协助。假设组内某个实例挂掉了,Kafka 能够自动检测到,然后把这个 Failed 实例之前负责的分区转移给其他活着的消费者。这个过程就是 Kafka 中大名鼎鼎的“重平衡”(Rebalance)。嗯,其实既是大名鼎鼎,也是臭名昭著,因为由重平衡引发的消费者问题比比皆是。事实上,目前很多重平衡的 Bug 社区都无力解决。
每个消费者在消费消息的过程中必然需要有个字段记录它当前消费到了分区的哪个位置上,这个字段就是消费者位移(Consumer Offset)。注意,这和上面所说的位移完全不是一个概念。上面的“位移”表征的是分区内的消息位置,它是不变的,即一旦消息被成功写入到一个分区上,它的位移值就是固定的了。而消费者位移则不同,它可能是随时变化的,毕竟它是消费者消费进度的指示器嘛。另外每个消费者有着自己的消费者位移,因此一定要区分这两类位移的区别。我个人把消息在分区中的位移称为分区位移,而把消费者端的位移称为消费者位移。
# 2.6 小结
小结一下今天提到的名词:
- 消息:Record。Kafka 是消息引擎嘛,这里的消息就是指 Kafka 处理的主要对象。
- 主题:Topic。主题是承载消息的逻辑容器,在实际使用中多用来区分具体的业务。
- 分区:Partition。一个有序不变的消息序列。每个主题下可以有多个分区。
- 消息位移:Offset。表示分区中每条消息的位置信息,是一个单调递增且不变的值。
- 副本:Replica。Kafka 中同一条消息能够被拷贝到多个地方以提供数据冗余,这些地方就是所谓的副本。副本还分为领导者副本和追随者副本,各自有不同的角色划分。副本是在分区层级下的,即每个分区可配置多个副本实现高可用。
- 生产者:Producer。向主题发布新消息的应用程序。
- 消费者:Consumer。从主题订阅新消息的应用程序。
- 消费者位移:Consumer Offset。表征消费者消费进度,每个消费者都有自己的消费者位移。
- 消费者组:Consumer Group。多个消费者实例共同组成的一个组,同时消费多个分区以实现高吞吐。
- 重平衡:Rebalance。消费者组内某个消费者实例挂掉后,其他消费者实例自动重新分配订阅主题分区的过程。Rebalance 是 Kafka 消费者端实现高可用的重要手段。
下图展示了上面提到的概念:
Question
Question: 为什么 Kafka 不像 MySQL 那样允许追随者副本对外提供读服务?
如果允许follower副本对外提供读服务(主写从读),首先会存在数据一致性的问题,消息从主节点同步到从节点需要时间,可能造成主从节点的数据不一致。主写从读无非就是为了减轻leader节点的压力,将读请求的负载均衡到follower节点,如果Kafka的分区相对均匀地分散到各个broker上,同样可以达到负载均衡的效果,没必要刻意实现主写从读增加代码实现的复杂程度。
# 3. Kafka 只是消息引擎系统吗?
纵观 Kafka 的发展脉络,它的确是从消息引擎起家的,但正如文章标题所问,Apache Kafka 真的只是消息引擎吗?这个问题的答案是,Apache Kafka 是消息引擎系统,也是一个分布式流处理平台(Distributed Streaming Platform)。
Kafka 自诞生伊始是以消息引擎系统的面目出现在大众视野中的。如果翻看 0.10.0.0 之前的官网说明,你会发现 Kafka 社区将其清晰地定位为一个分布式、分区化且带备份功能的提交日志(Commit Log)服务。Kafka 在设计之初就旨在提供三个方面的特性:
- 提供一套 API 实现生产者和消费者;
- 降低网络传输和磁盘存储开销;
- 实现高伸缩性架构。
开源之后的 Kafka 被越来越多的公司应用到它们企业内部的数据管道中,特别是在大数据工程领域,Kafka 在承接上下游、串联数据流管道方面发挥了重要的作用:所有的数据几乎都要从一个系统流入 Kafka 然后再流向下游的另一个系统中。这样的使用方式屡见不鲜以至于引发了 Kafka 社区的思考:与其我把数据从一个系统传递到下一个系统中做处理,我为何不自己实现一套流处理框架呢?基于这个考量,Kafka 社区于 0.10.0.0 版本正式推出了流处理组件 Kafka Streams,也正是从这个版本开始,Kafka 正式“变身”为分布式的流处理平台,而不仅仅是消息引擎系统了。今天 Apache Kafka 是和 Apache Storm、Apache Spark 和 Apache Flink 同等级的实时流处理平台。
你可能会有这样的疑问:作为流处理平台,Kafka 与其他主流大数据流式计算框架相比,优势在哪里呢?我能想到的有两点:
- 第一点是更容易实现端到端的正确性(Correctness)。Google 大神 Tyler 曾经说过,流处理要最终替代它的“兄弟”批处理需要具备两点核心优势:要实现正确性和提供能够推导时间的工具。实现正确性是流处理能够匹敌批处理的基石。正确性一直是批处理的强项,而实现正确性的基石则是要求框架能提供精确一次处理语义,即处理一条消息有且只有一次机会能够影响系统状态。目前主流的大数据流处理框架都宣称实现了精确一次处理语义,但这是有限定条件的,即它们只能实现框架内的精确一次处理语义,无法实现端到端的。
这是为什么呢?因为当这些框架与外部消息引擎系统结合使用时,它们无法影响到外部系统的处理语义,所以如果你搭建了一套环境使得 Spark 或 Flink 从 Kafka 读取消息之后进行有状态的数据计算,最后再写回 Kafka,那么你只能保证在 Spark 或 Flink 内部,这条消息对于状态的影响只有一次。但是计算结果有可能多次写入到 Kafka,因为它们不能控制 Kafka 的语义处理。相反地,Kafka 则不是这样,因为所有的数据流转和计算都在 Kafka 内部完成,故 Kafka 可以实现端到端的精确一次处理语义。
- 可能助力 Kafka 胜出的第二点是它自己对于流式计算的定位。官网上明确标识 Kafka Streams 是一个用于搭建实时流处理的客户端库而非是一个完整的功能系统。这就是说,你不能期望着 Kafka 提供类似于集群调度、弹性部署等开箱即用的运维特性,你需要自己选择适合的工具或系统来帮助 Kafka 流处理应用实现这些功能。
读到这你可能会说这怎么算是优点呢?坦率来说,这的确是一个“双刃剑”的设计,也是 Kafka 社区“剑走偏锋”不正面 PK 其他流计算框架的特意考量。大型公司的流处理平台一定是大规模部署的,因此具备集群调度功能以及灵活的部署方案是不可或缺的要素。但毕竟这世界上还存在着很多中小企业,它们的流处理数据量并不巨大,逻辑也并不复杂,部署几台或十几台机器足以应付。在这样的需求之下,搭建重量级的完整性平台实在是“杀鸡焉用牛刀”,而这正是 Kafka 流处理组件的用武之地。因此从这个角度来说,未来在流处理框架中,Kafka 应该是有一席之地的。
这一章说了这么多,其实就是想阐述一个观点:Apache Kafka 从一个优秀的消息引擎系统起家,逐渐演变成现在分布式的流处理平台。你不仅要熟练掌握它作为消息引擎系统的非凡特性及使用技巧,最好还要多了解下其流处理组件的设计与案例应用。
# 4. 我应该选择哪种 Kafka?
如果我们把视角从流处理平台扩展到流处理生态圈,Kafka 更是还有很长的路要走。前面我提到过 Kafka Streams 组件,正是它提供了 Kafka 实时处理流数据的能力。但是其实还有一个重要的组件我没有提及,那就是 Kafka Connect。
我们在评估流处理平台的时候,框架本身的性能、所提供操作算子(Operator)的丰富程度固然是重要的评判指标,但框架与上下游交互的能力也是非常重要的。能够与之进行数据传输的外部系统越多,围绕它打造的生态圈就越牢固,因而也就有更多的人愿意去使用它,从而形成正向反馈,不断地促进该生态圈的发展。就 Kafka 而言,Kafka Connect 通过一个个具体的连接器(Connector),串联起上下游的外部系统。
整个 Kafka 生态圈如下图所示。值得注意的是,这张图中的外部系统只是 Kafka Connect 组件支持的一部分而已。目前还有一个可喜的趋势是使用 Kafka Connect 组件的用户越来越多,相信在未来会有越来越多的人开发自己的连接器。
说了这么多你可能会问这和今天的主题有什么关系呢?其实清晰地了解 Kafka 的发展脉络和生态圈现状,对于指导我们选择合适的 Kafka 版本大有裨益。下面我们就进入今天的主题——如何选择 Kafka 版本?
# 4.1 有几种 Kafka?
有许多组织或公司发布的不同 Kafka:
# 1)Apache Kafka
Apache Kafka 是最“正宗”的 Kafka,其他的发行版要么是原封不动地继承了 Apache Kafka,要么是在此之上扩展了新功能,总之 Apache Kafka 是我们学习和使用 Kafka 的基础。
# 2)Confluent Kafka
Confluent 公司专注于提供基于 Kafka 的企业级流处理解决方案,主要从事商业化 Kafka 工具开发,并在此基础上发布了 Confluent Kafka。Confluent Kafka 提供了一些 Apache Kafka 没有的高级特性,比如跨数据中心备份、Schema 注册中心以及集群监控工具等。
题外话,Confluent 公司的创始人饶军是我们中国人,清华大学毕业的大神级人物。我们已经看到越来越多的 Apache 顶级项目创始人中出现了中国人的身影,另一个例子就是 Apache Pulsar,它是一个以打败 Kafka 为目标的新一代消息引擎系统。至于在开源社区中活跃的国人更是数不胜数,这种现象实在令人振奋。
# 3)Cloudera/Hortonworks Kafka
Cloudera 提供的 CDH 和 Hortonworks 提供的 HDP 这两个大数据平台都集成了 Apache Kafka。
# 4.2 特点比较
说完了目前市面上的这些 Kafka,我来对比一下它们的优势和劣势。
# 1)Apache Kafka 的特点
对 Apache Kafka 而言,它现在依然是开发人数最多、版本迭代速度最快的 Kafka。
但是 Apache Kafka 的劣势在于它仅仅提供最最基础的组件,特别是对于前面提到的 Kafka Connect 而言,社区版 Kafka 只提供一种连接器,即读写磁盘文件的连接器,而没有与其他外部系统交互的连接器,在实际使用过程中需要自行编写代码实现,这是它的一个劣势。
总而言之,如果你仅仅需要一个消息引擎系统亦或是简单的流处理应用场景,同时需要对系统有较大把控度,那么我推荐你使用 Apache Kafka。
# 2)Confluent Kafka 的特点
Confluent Kafka 目前分为免费版和企业版两种。前者和 Apache Kafka 非常相像,除了常规的组件之外,免费版还包含 Schema 注册中心和 REST proxy 两大功能。前者是帮助你集中管理 Kafka 消息格式以实现数据前向 / 后向兼容;后者用开放 HTTP 接口的方式允许你通过网络访问 Kafka 的各种功能,这两个都是 Apache Kafka 所没有的。
除此之外,免费版包含了更多的连接器,它们都是 Confluent 公司开发并认证过的,你可以免费使用它们。至于企业版,它提供的功能就更多了。在我看来,最有用的当属跨数据中心备份和集群监控两大功能了。多个数据中心之间数据的同步以及对集群的监控历来是 Kafka 的痛点,Confluent Kafka 企业版提供了强大的解决方案帮助你“干掉”它们。
不过 Confluent Kafka 的一大缺陷在于,Confluent 公司暂时没有发展国内业务的计划,相关的资料以及技术支持都很欠缺,很多国内 Confluent Kafka 使用者甚至无法找到对应的中文文档,因此目前 Confluent Kafka 在国内的普及率是比较低的。
一言以蔽之,如果你需要用到 Kafka 的一些高级特性,那么推荐你使用 Confluent Kafka。
# 3)CDH/HDP Kafka
这些大数据平台天然集成了 Apache Kafka,通过便捷化的界面操作将 Kafka 的安装、运维、管理、监控全部统一在控制台中。如果你是这些平台的用户一定觉得非常方便,因为所有的操作都可以在前端 UI 界面上完成,而不必去执行复杂的 Kafka 命令。另外这些平台提供的监控界面也非常友好,你通常不需要进行任何配置就能有效地监控 Kafka。
同时这样做的结果是直接降低了你对 Kafka 集群的掌控程度,而且在具有滞后性。
简单来说,如果你需要快速地搭建消息引擎系统,或者你需要搭建的是多框架构成的数据平台且 Kafka 只是其中一个组件,那么我推荐你使用这些大数据云公司提供的 Kafka。
# 5. Kafka 的版本号
这一章主要说一下 Apache Kafka 版本号的问题。
# 5.1 Kafka 版本命名
当前 Apache Kafka 已经迭代到 2.2 版本,社区正在为 2.3.0 发版日期进行投票,相信 2.3.0 也会马上发布。但是稍微有些令人吃惊的是,很多人对于 Kafka 的版本命名理解存在歧义。比如我们在官网上下载 Kafka 时,会看到这样的版本:
于是有些同学就会纳闷,难道 Kafka 版本号不是 2.11 或 2.12 吗?其实不然,前面的版本号是编译 Kafka 源代码的 Scala 编译器版本。Kafka 服务器端的代码完全由 Scala 语言编写,Scala 同时支持面向对象编程和函数式编程,用 Scala 写成的源代码编译之后也是普通的“.class”文件,因此我们说 Scala 是 JVM 系的语言,它的很多设计思想都是为人称道的。
回到刚才的版本号讨论。现在你应该知道了对于 kafka-2.11-2.1.1 的提法,真正的 Kafka 版本号实际上是 2.1.1。那么这个 2.1.1 又表示什么呢?前面的 2 表示大版本号,即 Major Version;中间的 1 表示小版本号或次版本号,即 Minor Version;最后的 1 表示修订版本号,也就是 Patch 号。Kafka 社区在发布 1.0.0 版本后特意写过一篇文章,宣布 Kafka 版本命名规则正式从 4 位演进到 3 位,比如 0.11.0.0 版本就是 4 位版本号。
坦率说,这里我和社区的意见是有点不同的。在我看来像 0.11.0.0 这样的版本虽然有 4 位版本号,但其实它的大版本是 0.11,而不是 0,所以如果这样来看的话 Kafka 版本号从来都是由 3 个部分构成,即“大版本号 - 小版本号 - Patch 号”。这种视角可以统一所有的 Kafka 版本命名,也方便我们日后的讨论。我们来复习一下,假设碰到的 Kafka 版本是 0.10.2.2,你现在就知道了它的大版本是 0.10,小版本是 2,总共打了两个大的补丁,Patch 号是 2。
# 5.2 Kafka 版本演进
Kafka 目前总共演进了 7 个大版本,分别是 0.7、0.8、0.9、0.10、0.11、1.0 和 2.0,其中的小版本和 Patch 版本很多。哪些版本引入了哪些重大的功能改进?关于这个问题,我建议你最好能做到如数家珍,因为这样不仅令你在和别人交谈 Kafka 时显得很酷,而且如果你要向架构师转型或者已然是架构师,那么这些都是能够帮助你进行技术选型、架构评估的重要依据。
Kafka 从 0.7 时代演进到 0.8 之后正式引入了副本机制,至此 Kafka 成为了一个真正意义上完备的分布式高可靠消息队列解决方案。有了副本备份机制,Kafka 就能够比较好地做到消息无丢失。那时候生产和消费消息使用的还是老版本的客户端 API,所谓的老版本是指当你用它们的 API 开发生产者和消费者应用时,你需要指定 ZooKeeper 的地址而非 Broker 的地址。
如果你现在尚不能理解这两者的区别也没关系,我会在专栏的后续文章中详细介绍它们。老版本客户端有很多的问题,特别是生产者 API,它默认使用同步方式发送消息,可以想见其吞吐量一定不会太高。虽然它也支持异步的方式,但实际场景中可能会造成消息的丢失,因此 0.8.2.0 版本社区引入了新版本 Producer API,即需要指定 Broker 地址的 Producer。
据我所知,国内依然有少部分用户在使用 0.8.1.1、0.8.2 版本。我的建议是尽量使用比较新的版本。如果你不能升级大版本,我也建议你至少要升级到 0.8.2.2 这个版本,因为该版本中老版本消费者 API 是比较稳定的。另外即使你升到了 0.8.2.2,也不要使用新版本 Producer API,此时它的 Bug 还非常多。
时间来到了 2015 年 11 月,社区正式发布了 0.9.0.0 版本。在我看来这是一个重量级的大版本更迭,0.9 大版本增加了基础的安全认证 / 权限功能,同时使用 Java 重写了新版本消费者 API,另外还引入了 Kafka Connect 组件用于实现高性能的数据抽取。如果这么多眼花缭乱的功能你一时无暇顾及,那么我希望你记住这个版本的另一个好处,那就是新版本 Producer API 在这个版本中算比较稳定了。如果你使用 0.9 作为线上环境不妨切换到新版本 Producer,这是此版本一个不太为人所知的优势。但和 0.8.2 引入新 API 问题类似,不要使用新版本 Consumer API,因为 Bug 超多的,绝对用到你崩溃。即使你反馈问题到社区,社区也不会管的,它会无脑地推荐你升级到新版本再试试,因此千万别用 0.9 的新版本 Consumer API。对于国内一些使用比较老的 CDH 的创业公司,鉴于其内嵌的就是 0.9 版本,所以要格外注意这些问题。
0.10.0.0 是里程碑式的大版本,因为该版本引入了 Kafka Streams。从这个版本起,Kafka 正式升级成分布式流处理平台,虽然此时的 Kafka Streams 还基本不能线上部署使用。0.10 大版本包含两个小版本:0.10.1 和 0.10.2,它们的主要功能变更都是在 Kafka Streams 组件上。如果你把 Kafka 用作消息引擎,实际上该版本并没有太多的功能提升。不过在我的印象中自 0.10.2.2 版本起,新版本 Consumer API 算是比较稳定了。如果你依然在使用 0.10 大版本,我强烈建议你至少升级到 0.10.2.2 然后使用新版本 Consumer API。还有个事情不得不提,0.10.2.2 修复了一个可能导致 Producer 性能降低的 Bug。基于性能的缘故你也应该升级到 0.10.2.2。
在 2017 年 6 月,社区发布了 0.11.0.0 版本,引入了两个重量级的功能变更:一个是提供幂等性 Producer API 以及事务(Transaction) API;另一个是对 Kafka 消息格式做了重构。
前一个好像更加吸引眼球一些,毕竟 Producer 实现幂等性以及支持事务都是 Kafka 实现流处理结果正确性的基石。没有它们,Kafka Streams 在做流处理时无法向批处理那样保证结果的正确性。当然同样是由于刚推出,此时的事务 API 有一些 Bug,不算十分稳定。另外事务 API 主要是为 Kafka Streams 应用服务的,实际使用场景中用户利用事务 API 自行编写程序的成功案例并不多见。
第二个重磅改进是消息格式的变化。虽然它对用户是透明的,但是它带来的深远影响将一直持续。因为格式变更引起消息格式转换而导致的性能问题在生产环境中屡见不鲜,所以你一定要谨慎对待 0.11 版本的这个变化。不得不说的是,这个版本中各个大功能组件都变得非常稳定了,国内该版本的用户也很多,应该算是目前最主流的版本之一了。也正是因为这个缘故,社区为 0.11 大版本特意推出了 3 个 Patch 版本,足见它的受欢迎程度。我的建议是,如果你对 1.0 版本是否适用于线上环境依然感到困惑,那么至少将你的环境升级到 0.11.0.3,因为这个版本的消息引擎功能已经非常完善了。
最后我合并说下 1.0 和 2.0 版本吧,因为在我看来这两个大版本主要还是 Kafka Streams 的各种改进,在消息引擎方面并未引入太多的重大功能特性。Kafka Streams 的确在这两个版本有着非常大的变化,也必须承认 Kafka Streams 目前依然还在积极地发展着。如果你是 Kafka Streams 的用户,至少选择 2.0.0 版本吧。
去年 8 月国外出了一本书叫 Kafka Streams in Action(中文版:《Kafka Streams 实战》),它是基于 Kafka Streams 1.0 版本撰写的。最近我用 2.0 版本去运行书中的例子,居然很多都已经无法编译了,足见两个版本变化之大。不过如果你在意的依然是消息引擎,那么这两个大版本都是适合于生产环境的。
最后还有个建议,不论你用的是哪个版本,都请尽量保持服务器端版本和客户端版本一致,否则你将损失很多 Kafka 为你提供的性能优化收益。
# 5.3 小结
我希望现在你对如何选择合适的 Kafka 版本能做到心中有数了。每个 Kafka 版本都有它恰当的使用场景和独特的优缺点,切记不要一味追求最新版本。事实上我周围的很多工程师都秉承这样的观念:不要成为最新版本的“小白鼠”。了解了各个版本的差异之后,我相信你一定能够根据自己的实际情况作出最正确的选择。