Kafka消息队列模型,帮助你快速了解消息队列模型

217 阅读11分钟

最近在学习消息队列方面的知识,因此想做个总结归纳,欢迎大家评论区随时指正~

一、Kafka流式数据平台

Kafka作为一个流式数据平台,拥有流处理能力,能够对实时的事件流进行流式地处理与分析,此外,kafka提供了事件流的发布与订阅功能,支持数据流的写入与写出,并且kafka能够将数据流存储起来,保证了故障容错。

什么是流式数据?

流式数据‌是指实时或接近实时生成和传输的数据形式,其特点是连续不断地生成和传输,而不是批量处理。

什么是流式数据平台?

流式数据平台是一种处理和管理实时数据流的系统。与传统的批处理系统不同,流式数据平台能够实时接收、处理和分析数据流,使得用户能够在数据生成的瞬间获取洞察和做出反应。

流式数据平台的特点有哪些?

  • 能够提供数据流的发布与订阅,具备数据注入功能。
  • 存储数据流的节点具有故障容错的特点,具备数据存储功能。
  • 能够对实时的数据流进行流式处理与分析,具备流处理功能。

Kafka是如何实现并组合流式数据平台的特点呢?

消息系统

消息系统(消息队列)有两种消息模型:

  • 队列模型:多个消费者读取消息时,队列(分区)中的每条消息只会发送给一个消费者。
  • 发布订阅模型:多个消费者订阅主题时,主题的每条消息都会发布给所有的消费者。

kafka使用消费组(consumer group)的概念,将队列与发布订阅两种消息模型进行统一。多个消费组内包含多个消费者,这样的组合既可以线性扩展消息处理的能力(能够同时处理多条消息),也能允许消息被多个消费组订阅。

  • kafka在使用队列模型时,会将消息平均分配给消费组的消费者来处理消息。
  • kafka在使用发布订阅模型时,会将消息广播给多个消费组。

image.png

存储系统

我们知道消息队列有个重要的特性就是能够解耦合,在生产者发布消息到消费者消费消息的期间,消息需要存放在消息队列中,即消息队列也需要承担存储的角色,负责保存还没有并消费的消息。

如果消息放在内存中,一旦服务器宕机或者进程重启,内存中存放的消息则会全部丢失。数据在写入Kafka集群的服务器节点时,会复制多份相同的副本消息保存,从而保证出现故障时消息不丢失。

为了保证消息的可靠存储,Kafka还支持确认应答模式,允许生产者的生产消息请求在收到Kafka的应答结果之前,阻塞地等待消息的写入结果,直到消息完全地复制到一个或者多个节点上,才认为该消息写入Kafka成功。

流处理系统

流式数据平台除了消息的读取和写入、消息的存储之外,还需要能够实时的流式数据处理能力。

对于简单的消息处理,Kafka提供了生产者与消费者的API提供消息的处理。对于复杂业务逻辑的处理,Kafka流处理提供了完整的流处理API,比如流的聚合、连接、各种转换操作等。

kafka将消息系统、存储系统、流处理系统都组合到一起,构成了以kafka为中心的流式数据处理平台。kafka既能处理最新的实时数据,也能处理过去存在的历史数据。

kafka作为流式数据平台的核心组件,主要包括四种核心API

  • 生产者应用程序发布数据流到kafka的一个或多个topic
  • 消费者应用程序订阅kafka的一个或多个topic,处理消息
  • 连接器将kafka主题与已有的数据源进行连接,数据可以进写入与写出队列
  • 流处理从kafka主题消费输入流,处理后,产生输出流到输出主题

二、Kafka基本模型

分区模型

kafka集群由多个消息代理服务器(broker server)组成,每条消息会发布到kafka集群的一个主题(topic)中。不同的应用如果产生不同类型的数据,则可以设置不同的topic来进行区分。一个topic一般会有多个消费者来消费topic中的消息数据,订阅了这个topic的消费者都可以接收并消费该topic中的数据。

kafka的每个主题Topic中维护了多个分区(partition),每个分区都是一个有序、不可变的消息序列,新的消息会不断追加到分区中。分区的每条消息都会按照时间顺序分配到一个单调递增的顺序编号,叫做偏移量(offset),这个偏移量能够唯一地定位当前分区中的每条消息。

image.png

如上图,主题Topic有2个分区,每个分区的偏移量从0开始,不同的分区之间的偏移量互相独立,互不影响。

一般来说,消息发送是通过异步发送给消费者进行消费,如果有多个消费者消费同一个消息队列,服务端会以消息存储的顺序依次发给消费者,这个时候消息到达消费者可能是无序的,这意味着在并行消费时,可能无法很好地保证消息被顺序处理。

Kafka在这点上,使用主题topic的分区partition作为消息并行处理的单元,以分区作为最小力度,将每个分区分配给消费组组中不同的消费者,并且一个分区指定唯一一个消费者,确保一个分区只属于一个消费者,即这个消费者是这个分区的唯一消费线程。

只要分区的消息是有序的,消费者处理消息的顺序性就有了保证。每个主题有多个分区,不同的消费者处理不同的分区,因此,kafka不仅保证了消息的有序性,也做到了消费的负载均衡。

消费模型

生产者将消息发送到kafka集群,消费者会将消息进行消费。消息的消费模型有两种

  • 推送模型
  • 拉取模型
推送模型

基于推送模型的消息,由消息代理来记录消费者的消费状态。消息代理在将消息推送到消费者后,则会标记该消息为已消费,但这种方式无法保证消息是否已完全消费,例如,消息推送给消费者后,当消费者进程挂掉或者由于网络原因而导致消费者没有收到这条消息时,则可能会导致消息丢失,因为在消息代理看来这条消息已经被标记为已消费,但消费者并没有实际处理这条消息。

如果要保证消息能够被实际处理,在消息队列发送完消息后,只有收到消费者的确认应答后,才将该消息设置为已消费,这就需要在消息队列中记录每条消息的状态,这种做法也会比较的麻烦。

拉取模型

kakfa采用拉取模型,由消费者自己记录消费状态,每个消费者互相独立地顺序读取对应分区的消息,消费者会自己记录当前的消费进度。

如下图,消费者拉取分区的消息,消费者的消费进度为5,消费者拉取消息的最小上限通过最高水位来进行空闲,生产者在写入消息时,如果消息没有达到指定的备份副本数量,则该消息对消费者是不可见的。

image.png

这种由消费者控制消费偏移量的优点在于消费者能够按照任意的顺序位置来消费消息。比如消费者可以重置到旧的偏移量来重新处理之前已经消费过的消息,或者直接跳到最近的位置开始消费消息。

有些消息系统中,消息代理会在消息被消费之后立即删除消息。如果有不同类型的消费者订阅该主题,则消息代理可能需要冗余存储同一条消息,或者等所有订阅的消费者消费完消息后才能删除消息,这就需要消息代理跟踪每一个消费者的消费状态,这样的设计很大程度上限制了消息系统的整体吞吐量和处理延迟。

kafka在这方面,采取生产者发布的所有消息会一直保存在kafka集群中,无论消息是否被消费。用户同时也可以通过设置消息的保留时间来清理过期或者不需要的消息数据,这样在消息发布后,它可以被不同的消费者进行消费,在一段时候过期消息就会被自动清理掉。

分布式模型

kafka每个主题中的多个分区日志分布式地存储在kafka集群中,为了保证故障容错,每个分区都会以副本的方式将消息复制到多个消息代理节点上。其中一个节点为主副本,其他副本作为备份副本(从副本)。

主副本会负责所有客户端的读写操作,备份副本仅仅只是从主副本同步数据过来。当主副本出现故障时,备份副本中的一个副本则会被选为新的主副本。因为每个分区的副本中只有主副本接收读写请求,所有每个服务端都会作为某些分区的主副本,以及另外一些分区的备份副本,这样kafka集群的所有服务端节点整体上对客户端是负载均衡的。

生产者和消费者相对于kafka服务端而言都是客户端,生产者客户端发布消息到服务端的指定主题,消息发送到指定分区。生产者发布消息时会根据一定的策略来发送消息,例如消息没有键时,会通过轮训方式进行客户端负载均衡,若消息有键则会跟进策略确保相同键的消息总是发送到同一个分区。

Kafka的消费者通过订阅主题来消费消息,并且每个消费者都会设置一个消费组名称。因为生产者发布到主题的每一条消息都只会发送给消费组的一个消费者。

如果要实现传统消息系统的"队列"模型,可以让每个消费者都拥有相同的消费组名称,这样消息就会负载均衡到所有的消费者。如果要实现"发布-订阅"模型,则每个消费者的消费组名称都不相同,这样每条消息就会广播给所有的消费者。

主题中的每个分区是消费者线程的最小并行单元。生产者发布消息到消息队列服务器的多个分区时,可以设置多个消费者来消费多个分区的消息,增加服务器节点可以提升集群的性能,增加消费者数量可以提升消息的处理能力。

image.png

同一个消费组下的多个消费者互相协调消费工作,kafka会将主题内的所有分区平均地分配给所有消费者,每个消费者可以分配到数量均等的分区,一个分区只对应一个消费者,而一个消费者可以消费多个分区的消息。

kafka的消费组管理协议会动态地维护消费组的消费者,当有新消费者加入消费组,或者有消费者离开消费组,都会触发再平衡操作。

kafka的消费者消费消息时,只保证在一个分区内消息的完全有序性,并不保证同一个主题中多个分区的消息顺序。而且,消费者读取一个分区消息的顺序和生产者写入到这个分区的消息顺序是一致的。比如,生产者依次写入"hello"和"kafka"两条消息到分区P1,则消费该分区的消费者读取到的顺序也一定是"hello"和"kafka"

如果业务上需要保证所有消息完全一致,只能通过将顺序消息写入到同一个分区完成,但这种做法的缺点是最多只能有一个消费者进行消费。

一般来说,只需要保证每个分区的有序性,再对消息加上键来保证相同键的所有消息落入同一个分区,就可以满足绝大多数的应用。