MQ(消息中间件)是项目开发中经常用到的一种技术。它是分布式系统重要的组件,主要用来解决应用解耦、异步任务、流量削锋等业务场景。MQ有非常多的开源实现,包括RocketMq、RabitMq、ActiveMq、Kafka等。在这些开源实现的组件中,Kafka是最流行的消息中间件,它以高吞吐、低延迟、可扩展、数据持久化等特点而受到开发人员的喜欢。每秒可处理10万条消息,这个级别可以满足大多数业务场景的需求。那Kafka是如何做到如此大的吞吐?Java语言中我们该如何使用Kafka呢?本文就将详细讲解这些知识。
Kafka 是什么?
LinkedIn(中文名为领英)是一家类似于FaceBook的社群网络,其用户群体大多是企业白领商业人士。通过在LinkedIn上注册账户,商业人士可以获取自己一个电子名片,维护他们在商业交往中认识并信任的联系人,这些人被称为“人脉”(Connections)。在LinkedIn网站上,每天活跃着大量的用户,这些用户每天都会产生海量的活动流数据,例如:访问页面操作、查看历史以及搜索内容等。同时,LinkedIn网站背后有一个庞大的计算机集群支持,公司运维人员需要监控每台机器的性能数据,包括CPU使用率、内存使用率、机器负载、服务器日志等。通常,用户活动流数据和服务器运维数据都是以文件的形式写入到日志,然后周期性的对这些文件进行分析得到最终的报表数据,这是一项复杂极其复杂且庞大的计算任务。为了提供更好的解决方案,LinkedIn的首席架构师Jay Kreps组件团队开发了分布式发布订阅消息系统Kafka。
Kafka一种高性能分布式基于发布/订阅的消息系统,采用Java和Scala语言开发。高吞吐和低延迟是它的两个核心特性,也是MQ消息中间件需要解决的核心问题。Kafka的主要特性表现在以下几个方面:
Kafka 是什么?
Kafka的高性能得益于它的几大组件的设计。Kafka主要包括以下几大组件:
Kafka 架构设计
上图是Kafka的网络拓扑图。从图中可以看到,一个Kafka集群是由多个Kafka Broker组成的,集群的数量决定了整个集群的性能。项目实践中我们根据实际需求来决定集群规模,集群规模越大,吞吐率越大,当然Kafka支持水平扩展,可以根据实际需求来扩展集群数量。Producer是消费的生产方,Producer的应用类型比较多,如日志、前面提到的用户行为数据、服务器性能监控数据,这些数据通过Kafka Producer Api Push到Kafka的Broker。消息按照topic和partition存储在特定的broker上。
每个topic被分成多个partition,每个partition是以append log文件形式存储。Producer发送消息会被追加到log文件的尾部,每条消息在文件中的位置称为 offset(偏移量),offset 为一个 long 型的数字,它唯一标记一条消息。Append log是一种顺序写磁盘的机制,效率远高于随机写内存,这也是Kafka高吞吐的一个重要原因。
Consumer负责消费Broker中指定的Topic中的数据,因为Topic中的数据按照Partition分布式存储,所以程序可以根据Partition的数量来启动相应数量的Consumer个数来消费数据。如下图所示:
xiaomi_dispatch这个topic有15个partition,客户端就可以启动15个消费者(消费线程)来并行消费该topic下的消息,极大的提高消费速率。所以如果碰到消费者能力不足而导致kafka消息积压的时候,增加partition数量可以有效解决积压问题。
Consumer Group是consumer的集合,Consumer Group在Kafka中有着重要的作用。某个topic中的消息只能被Consumer Group中的一个Consumer消费,这样可以防止同一个Consumer Group中的多个Consumer重复消费消息。如果程序想要在多个地方消费某个topic中的消息,只要将Consumer放在不同的 Consumer Group即可。
Zookeeper是Kafka集群的注册中心,负责管理Kafka集群配置,包括broker 注册、topic 注册、producer、consumer 注册、维护 partition 与 consumer 的关系、记录消息消费的进度、producer 和 consumer 负载均衡等功能。Consumer在消费partition中的消息的时候,需要将每个partition的offset值记录到zookeeper中。当consumer 重启或者其它 consumer 重新接管该消息分区的消息消费权后,能够从之前的进度开始继续进行消息消费。ZooKeeper 记录 partition 与 consumer 之间的关系,每个 consumer 一旦确定了对一个 partition 的消费权力,需要将其 consumer ID 写入到 ZooKeeper 对应消息分区的临时节点上。
使用Kafka
Kafka提供了各种语言版本的SDK,服务端和客户端都很方便接入,当然Java也不例外。SpinrgBoot是目前最流行的Java 框架,其本身也集成了Kafka,利用相应的Jar包非常容易集成Kafka。在SpringBoot中有两种方式集成Kafka,本文以集成消费者来说明。
01
第一种方式
最简单的方式集成,基于 KafkaListener注解来实现。示例代码如下:
基于 KafkaListener注解来实现
通过KafkaListener注解可以让SpringBoot启动kafka客户端消费。Topics指定需要消费的topic,Concurrency配置partition的数量,可以启动相同数量的消费者来消费。
ContainerFactory来指定Kafka配置信息,示例代码如下:
这里需要配置Kafka集群地址、消费者组、每次消费的最大消息数、Offset提交方式等。
02
第二种方式
编程式。示例代码如下:
编程式
原理与第一种方式类似,不同的地方在于手动创建Consumer,然后启动线程死循环消费消息。这种方式比第一种方式更灵活,程序可以灵活的控制消费者线程数量。
总结
本文介绍了Kafka的产生背景、六大核心特性,其中高吞吐和低延迟是它最重要的两大特性,也是Kafka得到广泛使用的重要原因。通过分析Kafka组件和架构设计,可以看到高吞吐和低延迟特性主要是由于topic多partition的设计,partition的数量决定了消费的并行消费能力。消息以append log的形式追加到partition中,这是一种顺序写磁盘的机制,效率远高于随机写内存序。通过这些方式,Kafka达到了每秒可以处理10万条消息,在众多的项目中得到了广泛的应用。
附:本文主要简要介绍了Kafka使用场景的案例!具体实现原理与核心思想会在后续的话题中逐步拆解:Kafka 核心原理解读与实战
- END -