(一)概念理解
Apache Kafka是一种开源的分布式流处理平台,专为高性能、高吞吐量的实时数据处理而设计。它最初由LinkedIn公司开发,旨在解决其网站活动中产生的大量实时数据处理和传输问题,后来于2011年开源,并捐赠给了Apache软件基金会,逐渐发展成为大数据和实时数据管道领域的核心组件之一。
(1)产生背景
在Kafka诞生之前,很多大型互联网公司面临着处理海量实时数据的挑战,这些数据通常来源于用户活动跟踪、日志生成、传感器数据、金融交易等。传统的消息队列系统,如RabbitMQ或ActiveMQ,虽然能够处理消息传递,但在处理极高吞吐量、大规模数据存储以及实时分析方面显得力不从心。具体来说,这些挑战包括:
- 高吞吐量需求:传统的消息系统难以应对每秒数百万条消息的处理需求。
- 可扩展性问题:随着数据量的快速增长,系统需要能够容易地横向扩展。
- 数据持久化与实时处理:需要一种既能快速处理数据,又能保证数据可靠存储的解决方案,以便进行即时分析和事后分析。
- 复杂的数据流处理:随着业务需求的增长,单一的消息传递已不能满足,需要一个能够支持复杂数据处理逻辑的平台。
(2)关键特性
Kafka正是为了解决这些问题而设计的,它的关键特性包括:
- 高吞吐量:通过优化磁盘I/O、批量处理和零拷贝技术,Kafka能够达到非常高的数据处理速度。
- 分布式架构:支持数据的分区和复制,既提高了系统的可用性,也使得系统可以横向扩展以应对更大的数据量。
- 持久化与实时性:Kafka的消息被持久化到磁盘,并且支持实时消费,实现了数据的可靠存储与近实时处理的平衡。
- 灵活的消息模型:支持发布-订阅模式和队列模式,满足不同场景的需求。
- 生态丰富:Kafka不仅仅是消息队列,还发展出了Kafka Streams用于流处理,以及与其他大数据处理框架(如Apache Spark、Flink)的紧密集成,形成了强大的数据处理生态系统。
(3)应用场景
- 日志收集与处理: Kafka常用于收集各种服务的日志数据,便于日志分析和监控。
- 实时流处理: 结合Spark、Flink等流处理框架,Kafka可以用于实时数据分析和决策。
- 事件驱动架构: Kafka作为消息中间件,支撑微服务间的解耦通信,实现事件驱动的系统设计。
- 数据集成: Kafka可以作为不同数据源和数据仓库之间的桥梁,支持数据的实时同步和ETL流程。
- 用户活动跟踪:Kafka经常被用来记录web用户或者app用户的各种活动,如浏览网页、搜索、点击等活动,这些活动信息被各个服务器发布到kafka的topic中,然后订阅者通过订阅这些topic来做实时的监控分析,或者装载到hadoop、数据仓库中做离线分析和挖掘。
- 运营指标:Kafka也经常用来记录运营监控数据。包括收集各种分布式应用的数据,生产各种操作的集中反馈,比如报警和报告。
(二)消息队列的通信模式
(1)点对点(P2P)模式
在点对点模式中,消息队列扮演着“中间人”的角色,用于连接一个消息生产者(发送者)和一个或多个消息消费者(接收者),但是每条消息只会被一个消费者接收和处理。
(2)发布/订阅(Pub/Sub)模式
发布/订阅模式与点对点模式的主要区别在于消息的分发方式。在这种模式下,消息生产者发布消息到一个主题(Topic)上,所有订阅了这个主题的消费者都能收到该主题下的所有消息。
(三) Kafka设计架构
(1)基础架构与名词解释
- Producer:Producer即生产者,消息的产生者,是消息的入口。
- Broker:Broker是kafka实例,每个服务器上有一个或多个kafka的实例,我们姑且认为每个broker对应一台服务器。每个kafka集群内的broker都有一个不重复的编号,如图中的broker-0、broker-1等……
- Topic:消息的主题,可以理解为消息的分类,kafka的数据就保存在topic。在每个broker上都可以创建多个topic。
- Partition:Topic的分区,每个topic可以有多个分区,分区的作用是做负载,提高kafka的吞吐量。同一个topic在不同的分区的数据是不重复的,partition的表现形式就是一个一个的文件夹!
- Replication:每一个分区都有多个副本,副本的作用是做备胎。当主分区(Leader)故障的时候会选择一个备胎(Follower)上位,成为Leader。在kafka中默认副本的最大数量是10个,且副本的数量不能大于Broker的数量,follower和leader绝对是在不同的机器,同一机器对同一个分区也只可能存放一个副本(包括自己)。
- Message:每一条发送的消息主体。
- Consumer:消费者,即消息的消费方,是消息的出口。
- Consumer Group:我们可以将多个消费组组成一个消费者组,在kafka的设计中同一个分区的数据只能被消费者组中的某一个消费者消费。同一个消费者组的消费者可以消费同一个topic的不同分区的数据,这也是为了提高kafka的吞吐量!
- Zookeeper:kafka集群依赖zookeeper来保存集群的的元信息,来保证系统的可用性。
(2)工作流程分析
1.发送数据
我们看上面的架构图中,producer就是生产者,是数据的入口。注意看图中的箭头,Producer在写入数据的时候永远的找leader,不会直接将数据写入follower!那leader怎么找呢?写入的流程又是什么样的呢?我们看下图:
发送的流程就在图中已经说明了,就不单独在文字列出来了!需要注意的一点是,消息写入leader后,follower是主动的去leader进行同步的!producer采用push模式将数据发布到broker,每条消息追加到分区中,顺序写入磁盘,所以保证同一分区内的数据是有序的!写入示意图如下:
上面说到数据会写入到不同的分区,那kafka为什么要做分区呢?相信大家应该也能猜到,分区的主要目的是:
- 方便扩展:因为一个topic可以有多个partition,所以我们可以通过扩展机器去轻松的应对日益增长的数据量。
- 提高并发:以partition为读写单位,可以多个消费者同时消费数据,提高了消息的处理效率。
在Kafka中,当一个topic拥有多个partition时,producer会通过特定的策略决定数据发送至哪个:
-
指定分区(Manual Partitioning):生产者在发送消息时,可以明确指定消息应写入哪个分区。这种方式给予生产者最大的控制权,适用于需要确保某些消息逻辑上相邻或者实现特定消息处理顺序的场景。例如,如果消息关联到特定用户且希望该用户的所有消息保持顺序,可以通过用户ID作为分区键来实现。
-
基于键的分区(Key-based Partitioning):如果生产者没有明确指定分区,但是设置了消息的键(key),Kafka会使用该键的哈希值来决定消息的分区。这种方式可以自然地实现某种程度的消息排序和分组,因为具有相同键的消息会被发送到相同的分区。例如,使用用户ID作为键可以确保来自同一用户的请求被顺序处理,尽管这要求消费者端也要按照分区消费并处理消息顺序。
-
轮询分区(Round-Robin Partitioning):当生产者既没有指定分区,也没有为消息设置键时,Kafka会采用轮询的方式将消息均匀地分配到各个分区。这种方法简单且有效,可以很好地分散写入负载,确保没有单个分区过载,适合对消息顺序没有严格要求的场景。
Kafka通过ACK应答机制确保消息在生产者向队列写入时不丢失,允许用户根据可靠性需求选择不同级别的确认策略:
- acks=0策略牺牲数据安全性换取最高写入效率,不等待任何确认直接认为消息发送成功。
- acks=1策略在消息被首领节点接收后即确认,平衡了可靠性和性能,确保至少被一个副本接收。
- acks=all策略最为安全,需等待所有副本(包括首领和跟随者)确认消息,确保数据得到备份,但牺牲了一定的写入效率。
若尝试向未创建的Topic发送消息,Kafka默认配置下会自动创建该Topic,初始化其分区数为1,且副本数也为1,虽确保消息发送成功,但这种自动创建行为可能不符合特定场景的安全或性能要求,故生产环境中通常会预先定义Topic并配置合适的分区和副本数量。
2.保存数据
Producer将数据写入kafka后,集群就需要对数据进行保存了!kafka将数据保存在磁盘,可能在我们的一般的认知里,写入磁盘是比较耗时的操作,不适合这种高并发的组件。Kafka初始会单独开辟一块磁盘空间,顺序写入数据(效率比随机写入高)。
(四)Kafka 文件存储架构
这里比较好理解:
一个Topic分别存储在不同的partition中
一个partitioin对应着多个replica备份
一个replica对应着一个Log
一个Log对应多个LogSegment
而在LogSegment中存储着log文件、索引文件、其它文件
(1)Message结构
上面说到log文件就实际是存储message的地方,我们在producer往kafka写入的也是一条一条的message,那存储在log中的message是什么样子的呢?消息主要包含消息体、消息大小、offset、压缩类型……等等!我们重点需要知道的是下面三个:
- offset:offset是一个占8byte的有序id号,它可以唯一确定每条消息在parition内的位置!
- 消息大小:消息大小占用4byte,用于描述消息的大小。
- 消息体:消息体存放的是实际的消息数据(被压缩过),占用的空间根据具体的消息而不一样。
(2)存储策略
无论消息是否被消费,kafka都会保存所有的消息。那对于旧数据有什么删除策略呢?
- 基于时间,默认配置是168小时(7天)。
- 基于大小,默认配置是1073741824。
需要注意的是,kafka读取特定消息的时间复杂度是O(1),所以这里删除过期的文件并不会提高kafka的性能!
(五)Kafka 消费者架构
消费者使用一个 消费组 名称来进行标识,发布到topic中的每条记录被分配给订阅消费组中的一个消费者实例.消费者实例可以分布在多个进程中或者多个机器上。
如果所有的消费者实例在同一消费组中,消息记录会负载平衡到每一个消费者实例.
如果所有的消费者实例在不同的消费组中,每条消息记录会广播到所有的消费者进程.
消息存储在log文件后,消费者就可以进行消费了。在讲消息队列通信的两种模式的时候讲到过点对点模式和发布订阅模式。Kafka采用的是发布订阅模式,消费者主动的去kafka集群拉取消息,与producer相同的是,消费者在拉取消息的时候也是找leader去拉取。
(1)消费数据
多个消费者可以组成一个消费者组(consumer group),每个消费者组都有一个组id!同一个消费组者的消费者可以消费同一topic下不同分区的数据,但是不会组内多个消费者消费同一分区的数据!!!我们看下图:
图示是消费者组内的消费者小于partition数量的情况,所以会出现某个消费者消费多个partition数据的情况,消费的速度也就不及只处理一个partition的消费者的处理速度!如果是消费者组的消费者多于partition的数量,那会不会出现多个消费者消费同一个partition的数据呢?上面已经提到过不会出现这种情况!多出来的消费者不消费任何partition的数据。所以在实际的应用中,建议消费者组的consumer的数量与partition的数量一致!
参考文章:
Kafka基本原理详解(超详细!)_kafka工作原理-CSDN博客
Kafka 设计架构原理详细解析(超详细图解)_kafka架构原理-CSDN博客