本文参考尚硅谷大数据技术之Kafka。
消息队列
(1)点对点模式(一对一,消费者主动拉取数据,消息收到后消息清除)
点对点模型通常是一个基于拉取或者轮询的消息传送模型,这种模型从队列中请求信息,而不是将消息推送到客户端**。这个模型的特点是发送到队列的消息被一个且只有一个接收者接收处理,即使有多个消息监听者也是如此**。
(2)发布/订阅模式(一对多,数据生产后,推送给所有订阅者)
发布订阅模型则是一个基于推送的消息传送模型。发布订阅模型可以有多种不同的订阅者,临时订阅者只在主动监听主题时才接收消息,而持久订阅者则监听主题的所有消息,即使当前订阅者不可用,处于离线状态。
为什么需要消息队列(消息队列的优点)
-
解耦:允许你独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束。
-
冗余:消息队列把数据进行持久化直到它们已经被完全处理,通过这一方式规避了数据丢失风险。许多消息队列所采用的"插入-获取-删除"范式中,在把一个消息从队列中删除之前,需要你的处理系统明确的指出该消息已经被处理完毕,从而确保你的数据被安全的保存直到你使用完毕。
-
扩展性:因为消息队列解耦了你的处理过程,所以增大消息入队和处理的频率是很容易的,只要另外增加处理过程即可。
-
灵活性 & 峰值处理能力:在访问量剧增的情况下,应用仍然需要继续发挥作用,但是这样的突发流量并不常见。如果为以能处理这类峰值访问为标准来投入资源随时待命无疑是巨大的浪费。使用消息队列能够使关键组件顶住突发的访问压力,而不会因为突发的超负荷的请求而完全崩溃。
-
可恢复性:系统的一部分组件失效时,不会影响到整个系统。消息队列降低了进程间的耦合度,所以即使一个处理消息的进程挂掉,加入队列中的消息仍然可以在系统恢复后被处理。
-
顺序保证:在大多使用场景下,数据处理的顺序都很重要。大部分消息队列本来就是排序的,并且能保证数据会按照特定的顺序来处理。(例如,Kafka保证一个Partition内的消息的有序性)
-
缓冲:有助于控制和优化数据流经过系统的速度,解决生产消息和消费消息的处理速度不一致的情况。
-
异步通信:**很多时候,用户不想也不需要立即处理消息。消息队列提供了异步处理机制,允许用户把一个消息放入队列,但并不立即处理它。**想向队列中放入多少消息就放多少,然后在需要的时候再去处理它们。
什么是Kafka
-
Apache Kafka 是一个开源消息系统,由Scala写成。它是由Apache软件基金会开发的一个开源消息系统项目,主要用于缓存数据以支持流式计算。
-
发展历程:Kafka最初是由LinkedIn公司开发,并于2011年初开源。2012年10月,Kafka从Apache Incubator毕业。该项目的目标是为处理实时数据提供一个统一、高通量、低延迟的平台。
-
核心概念:
- Kafka是一个分布式消息队列。
- 在Kafka中,消息按Topic进行分类。
- 发送消息的角色称为Producer。
- 接收消息的角色称为Consumer。
- Kafka集群由多个Kafka实例组成,每个实例称为Broker。
-
系统依赖:
- Kafka集群和Consumer都依赖于Zookeeper集群来保存一些元信息(meta信息),以保证系统的可用性。
Kafka架构
-
Broker:一台Kafka服务器就是一个Broker。一个集群由多个Broker组成。一个Broker可以容纳多个Topic。
-
Producer:消息生产者,即向Kafka Broker发送消息的客户端。
-
Consumer:消息消费者,即从Kafka Broker取消息的客户端。
-
Consumer Group (CG):消费者组,由多个Consumer组成。消费者组是一组消费者实例,它们共同消费一个或多个topic,以达到高吞吐量和负载平衡。Kafka保证一个分区内的消息只被消费者组中的一个消费者读取,这意味着在一个消费者组内,每个分区的数据只被一个消费者处理。如果一个消费者组中有多个消费者实例,Kafka会自动平衡每个消费者负责的分区,以优化数据处理速度。消费者组允许多个消费者实例协作处理数据,这样可以在处理大量数据时,提供更高的并发度和数据吞吐量。当新的消费者加入组或者现有的消费者退出组时,分区所有权会在组内消费者之间重新平衡。
-
Topic:可以理解为一个队列,生产者和消费者面向的都是一个Topic。
-
Partition:为了实现扩展性,一个非常大的Topic可以分布到多个Broker(即服务器)上,一个Topic可以分为多个Partition,每个Partition储存不同的数据,每个Partition是一个有序的队列。
-
Replica:副本,为保证集群中的某个节点发生故障时,该节点上的Partition数据不丢失,且Kafka仍然能够继续工作,Kafka提供了副本机制,一个Topic的每个分区都有若干个副本,一个Leader和若干个Follower。
-
Leader:每个分区多个副本的“主”,生产者发送数据的对象,以及消费者消费数据的对象都是Leader。
-
Follower:每个分区多个副本中的“从”,实时从Leader中同步数据,保持和Leader数据的同步。Leader发生故障时,某个Follower会成为新的Leader。
Kafka生产过程分析
写入方式
producer采用推(push)模式将消息发布到broker,每条消息都被追加(append)到分区(patition)中,属于顺序写磁盘(顺序写磁盘效率比随机写内存要高,保障kafka吞吐率)。
分区(Partition)
消息发送时都被发送到一个topic,其本质就是一个目录,而topic是由一些Partition Logs(分区日志)组成,其组织结构如下图所示:
Kafka中的消息存储和处理是以分区(Partition)为基础的。每个分区中的消息都是有序的,并且每条消息都被赋予了一个唯一的offset值。以下是关于分区的原因和原则:
1. 分区的原因
- 扩展性:通过增加Partition,可以方便地在集群中进行扩展。每个Partition可以调整以适应它所在的机器,而一个Topic则可以由多个Partition组成,这样,整个集群就能够适应任意大小的数据。
- 并发性:由于读写操作可以以Partition为单位进行,因此可以提高并发性,从而提升整体系统的吞吐量。
2. 分区的原则
- 指定Partition:如果在生产消息时指定了Partition,则消息将直接发送到该Partition。
- 指定Key未指定Partition:如果未指定Partition但指定了Key,那么将通过对Key的值进行哈希(hash)处理来决定消息应该发送到哪个Partition。
- 未指定Partition和Key:如果既未指定Partition也未指定Key,那么将采用轮询方式(round-robin)来选择一个Partition。
通过这种方式,Kafka能够有效地处理大量数据,同时保持高效率和数据一致性。
副本(Replication)
在没有副本的情况下,一旦负责存储特定分区数据的服务器(Broker)发生故障,那么该分区的数据将无法访问,这不仅影响消费者(Consumer)获取数据,也阻止生产者(Producer)继续写入数据。引入副本之后,每个分区都会有多个副本,大大提高了数据的安全性和系统的稳定性。
- Leader和Follower:为了管理这些副本,Kafka为每个分区的副本集合选出一个领导者(Leader),其余的副本成为跟随者(Follower)。
- 数据交互:生产者(Producer)和消费者(Consumer)仅与领导者(Leader)进行交互,这包括数据的写入和读取。
- 数据复制:跟随者(Follower)负责从领导者(Leader)那里复制数据。即使在领导者发生故障时,由于存在跟随者的副本,Kafka仍能通过选举新的领导者来继续运行,而不会影响数据的可用性和服务的连续性。
通过引入副本机制,Kafka确保了即使在部分服务器宕机的情况下,数据和服务仍然是可用的,从而提高了整体系统的鲁棒性。
写入流程
在Apache Kafka中,副本机制的引入旨在提高数据可靠性和系统可用性。以下详细介绍了使用副本机制时,消息发布和确认的标准过程:
-
确定Leader:
- 生产者(Producer)首先需要从Zookeeper的
/brokers/.../state
节点查询到该分区(Partition)的领导者(Leader)。
- 生产者(Producer)首先需要从Zookeeper的
-
发送消息:
- 生产者将消息发送到这个确定的领导者(Leader)。
-
消息写入:
- 领导者(Leader)将收到的消息写入其本地日志(Log)。
-
消息复制:
- 跟随者(Followers)从领导者(Leader)那里拉取(Pull)消息,并将这些消息写入它们各自的本地日志。完成写入后,跟随者会向领导者发送确认(ACK)。
-
确认消息:
- 一旦领导者(Leader)从所有处于同步副本集(ISR, In-Sync Replicas)中的副本收到确认(ACK),它就会提升高水位标记(High Watermark),即最后提交的偏移量(Offset),然后向生产者发送确认(ACK)。
通过这一流程,Kafka确保了数据的可靠性和系统的高可用性,即使在部分节点宕机的情况下,服务也能正常运行。
Broker 保存消息
存储方式
物理上把topic分成一个或多个patition(对应 server.properties 中的num.partitions=3配置),每个patition物理上对应一个文件夹(该文件夹存储该patition的所有消息和索引文件),如下:
[atguigu@hadoop102 logs]$ ll
drwxrwxr-x. 2 atguigu atguigu 4096 8月 6 14:37 first-0
drwxrwxr-x. 2 atguigu atguigu 4096 8月 6 14:35 first-1
drwxrwxr-x. 2 atguigu atguigu 4096 8月 6 14:37 first-2
[atguigu@hadoop102 logs]$ cd first-0
[atguigu@hadoop102 first-0]$ ll
-rw-rw-r--. 1 atguigu atguigu 10485760 8月 6 14:33 00000000000000000000.index
-rw-rw-r--. 1 atguigu atguigu 219 8月 6 15:07 00000000000000000000.log
-rw-rw-r--. 1 atguigu atguigu 10485756 8月 6 14:33 00000000000000000000.timeindex
-rw-rw-r--. 1 atguigu atguigu 8 8月 6 14:37 leader-epoch-checkpoint
存储策略
无论消息是否被消费,kafka都会保留所有消息。有两种策略可以删除旧数据:
1)基于时间:log.retention.hours=168
2)基于大小:log.retention.bytes=1073741824
需要注意的是,因为Kafka读取特定消息的时间复杂度为O(1),即与文件大小无关,所以这里删除过期文件与提高 Kafka 性能无关。
Zookeeper存储结构
注意:producer不在zk中注册,消费者在zk中注册。
Kafka消费过程分析
kafka提供了两套consumer API:高级Consumer API和低级Consumer API。
高级API
优点
- 简化编程:使用高级API可以简化编程,不需要开发者自行管理offset,系统通过Zookeeper自动管理。
- 自动管理:不需要手动管理分区、副本等细节,系统会自动进行管理。
- 容错性:如果消费者断线,它会自动根据上一次记录在Zookeeper中的offset继续获取数据(默认设置每分钟更新一次Zookeeper中存储的offset)。
- 分组隔离:可以使用group来区分对同一个topic的不同程序访问,保证不同的group记录不同的offset,这样不同的程序读取同一个topic时不会因为offset互相影响。
缺点
- 限制性:不能自行控制offset,这对于需要进行特殊处理的场景可能是一个限制。
- 细节控制:不能细化控制如分区、副本、Zookeeper等详细操作。
低级API
1)低级 API 优点
- 能够让开发者自己控制offset,想从哪里读取就从哪里读取。 自行控制连接分区,对分区自定义进行负载均衡
- 对zookeeper的依赖性降低(如:offset不一定非要靠zk存储,自行存储offset即可,比如存在文件或者内存中)
2)低级API缺点
- 太过复杂,需要自行控制offset,连接哪个分区,找到分区leader 等。
消费者组
Kafka的消费者是按照消费者组(Consumer Group)的方式组织的,这意味着一个或多个消费者可以组成一个组来共同消费一个topic。
工作方式
- 分区所有权:每个分区在同一时间只能由一个消费者组内的消费者读取,确保消息的有序处理。但不同消费者组可以同时消费同一个分区,实现消息的多订阅。
- 组内消费:在一个消费者组内,消费者平均分配或根据策略分配topic的分区。例如,如果一个topic有三个分区,消费者组中有三个消费者,通常每个消费者会消费一个分区。
- 负载均衡:如果某个消费者失败,消费者组内的其他成员会自动重新分配失败消费者所负责的分区,这样保证了消费的连续性和高可用性。
水平扩展
- 通过增加消费者数量,消费者组可以水平扩展,以提高消息处理的速度和效率。
- 消费者组的水平扩展使得Kafka能够支持处理大量消息,满足高吞吐需求。
自动故障转移
- 如果消费者组中的某个消费者出现故障,剩余的消费者会自动接管故障消费者的分区,继续消费消息,这个过程称为自动负载均衡。
- 这种机制确保了即使在某些消费者失败的情况下,消息仍然可以被有效和及时地处理。
通过上述机制,Kafka的消费者组提供了一个强大、灵活和高效的方式来处理和消费大规模的消息流。
消费方式
consumer采用pull(拉)模式从broker中读取数据。
push(推)模式很难适应消费速率不同的消费者,因为消息发送速率是由broker决定的。它的目标是尽可能以最快速度传递消息,但是这样很容易造成consumer来不及处理消息,典型的表现就是拒绝服务以及网络拥塞。而pull模式则可以根据consumer的消费能力以适当的速率消费消息。
对于Kafka而言,pull模式更合适,它可简化broker的设计,consumer可自主控制消费消息的速率,同时consumer可以自己控制消费方式——即可批量消费也可逐条消费,同时还能选择不同的提交方式从而实现不同的传输语义。
pull模式不足之处是,如果kafka没有数据,消费者可能会陷入循环中,一直等待数据到达。为了避免这种情况,我们在我们的拉请求中有参数,允许消费者请求在等待数据到达的“长轮询”中进行阻塞(并且可选地等待到给定的字节数,以确保大的传输大小)。
参考
https://www.cnblogs.com/datadance/p/16292991.html
https://mantou.plus/2021/03/05/kafka%E6%9E%B6%E6%9E%84/
https://blog.csdn.net/philip502/article/details/118966065