消息中间件常见面试题及解答
- 1 Kafka
- 1.0 为什么使用kafka而不是RabbitMQ?
- 1.1 Kafka的特点
- 1.2 Kafka的应用场景
- 1.3 kafka的基本架构
- 1.4 Kafka的关键组件和概念
- 1.5 AR、ISR、OSR、HW、LEO
- 1.6 kafka中的HW和LEO的区别
- 1.7 kafka的复制机制
- 1.8 Kafka如何保证消息不丢失
- 1.9 Kafka如何保证消息不被重复消费
- 1.10 Kafka如何保证消息有序
- 1.11 kafka是如何根据消息key转发到指定分区
- 1.12 Kafka的Partition和同一个消费组中消费者数量的关系
- 1.13 Partition数量少于customer数量时,为什么会造成customer闲置
- 1.14 kafka的多分区,多副本如何实现容灾机制
- 1.15 Docker内查看Kafka数据
- 1.16 Kafka的存盘的策略?
- 1.17 有遇到过消费不均的情况吗?就可能多个消费分区,然后有些分区的量很多,有些消费分区的量那不太多的这种情况。
- 2 RabbitMQ
- 2.1 RabbitMQ和Kafka的区别
- 2.2 RabbitMQ的消息确认和Kafka的ack是一样的吗
- 2.3 为什么选择RabbitMQ而不是Kafka
- 2.4 RabbitMQ特点
- 2.5 RabbitMQ使用场景
- 2.6 RabbitMQ核心概念
- 2.7 Exchange说明
- 2.8 RabbitMQ的消息是基于什么传输
- 2.9 RabbitMQ如何保证消息不丢失、不重复消费
- 2.10 RabbitMQ如何保证消息的顺序性
- 2.11 RabbitMQ生产的时候有几种模式
- 2.12 RabbitMQ的消费者在经历了网络闪断之后还能继续消费,还是说需要服务重启后才能再消费
- 2.13 RabbitMQ集群
- 2.14 RabbitMQ的死信队列
- 2.15 RabbitMQ有哪些负载均衡策略?
- 2.16 RabbitMQ实现一个延时队列,该怎么去做?
- 3 设计一个抢红包系统
1 Kafka
Kafka
是一款基于发布与订阅的消息系统。kafka
是一个多分区
、多副本
且基于zookeeper
协调的分布式消息系统。也是一个分布式流式处理平台,它以高吞吐量
、可持久化
、可水平扩展
、支持流数据处理
等多种特性而被广泛使用。
1.0 为什么使用kafka而不是RabbitMQ?
适用于实时大数据,并且可以快速扩展,并发处理(也就是增加多个分区,同时再增加多个消费者)。
1.1 Kafka的特点
- 高吞吐量:
Kafka
能够处理大规模的消息流,每秒的处理数可达百万条消息。这使其适用于高流量的应用程序和大数据处理。 - 持久性:
Kafka
将消息持久存储在磁盘上,确保消息在发布后不会丢失。所以Kafka
可以用于关键业务数据的传输和存储。 - 分布式架构:
Kafka
是一个分布式系统,它允许数据分区和分布式处理。因此它可以轻松地扩展以适应不断增长的数据需求,并提供容错性和高可用性。 - 水平扩展性:
Kafka
可以通过添加更多的代理节点来实现水平扩展,以提高吞吐量和容量,而无需对现有应用程序进行大规模更改。 - 多主题支持:
Kafka
支持多个主题,每个主题可以包含一组相关的消息。这使得数据可以根据不同的应用程序或用例进行组织和隔离。 - 分区和复制:主题可以分为多个分区,每个分区可以复制到多个代理上,以提供容错性和数据冗余。这确保了数据的可用性和可靠性。
- 低延迟:
Kafka
提供低延迟的消息传递,适用于实时数据处理和事件驱动的应用程序。 - 消息保留策略:
Kafka
允许配置消息的保留策略,以确定消息在主题中保留的时间。这有助于控制存储成本和数据保留期限。 - 流处理支持:
Kafka
提供了与流处理框架(如KafkaStreams
和Apache Flink
)的集成,能够进行实时数据处理、转换和分析。 - 消费者组:
Kafka
支持消费者组,使多个消费者能够共同消费一个主题中的消息,以提供负载均衡和高可用性。 - 监控和管理工具:
Kafka
提供了监控和管理工具,帮助管理员跟踪性能指标、故障排除问题和进行配置管理。 - 社区支持:
Kafka
是一个开源项目,拥有庞大的社区,提供了广泛的文档和支持资源。
1.2 Kafka的应用场景
Kafka
消息中间件在各种应用场景中都具有广泛的用途,特别是在需要处理实时数据流、构建高吞吐量和可扩展应用程序时。以下是一些常见的Kafka
应用场景:
- 日志集中和分析:
Kafka
可以用于收集分布式应用程序和系统的日志数据,并将其传送到中央存储或分析平台。这有助于监控应用程序性能、故障排除和安全审计。 - 事件驱动架构:
Kafka
可以用于构建事件驱动的架构,使不同组件之间能够实时通信和协作。这对于构建实时数据处理流水线和微服务架构非常有用。 - 实时数据流处理:
Kafka
与流处理框架(如KafkaStreams
、Apache Flink
和Apache Spark
)集成,用于实时数据流处理和复杂事件处理。这些框架可以从Kafka
主题中读取数据并执行各种转换和计算。 - 实时监控和仪表盘:
Kafka
可以用于实时监控和生成仪表盘。通过将事件和指标数据发送到Kafka
主题,可以构建实时监控系统,以便及时检测和响应问题。 - 电信和网络分析:电信提供商可以使用
Kafka
来处理大规模的网络事件和信令数据,以监视网络性能、预测故障和优化资源分配。 - 物联网(IoT)数据处理:
Kafka
可以处理大规模的IoT
设备生成的数据流,用于物联网监控、智能城市、工业自动化和智能家居等应用。 - 日志和指标传输:
Kafka
可以用于将日志和性能指标数据从应用程序发送到中央存储或分析工具,帮助进行实时监控和故障排查。 - 数据湖构建:
Kafka
可以用作构建数据湖(Data Lake
)的一部分,将数据从不同来源导入到数据湖中,以便进行分析和报告。 - 分布式应用程序集成:
Kafka
可以用于在分布式应用程序之间传递数据,实现松耦合的集成,从而降低系统之间的依赖性。 - 实时广告投放:在线广告平台可以使用
Kafka
来处理广告请求、点击和展示数据,以实现实时广告投放和优化。 - 金融交易数据:金融机构可以使用
Kafka
来处理实时的交易数据、市场数据和风险管理数据,以支持高频交易和决策制定。
这些只是Kafka
的一些典型应用场景,但它在许多其他领域和用例中也具有价值。Kafka
的可扩展性、可靠性和高吞吐量特性使其成为处理实时数据的理想选择,特别是在需要处理大量数据并保持低延迟的情况下。
1.3 kafka的基本架构
一个典型的Kafka
体系架构包括若干Producer
、若干Broker
、若干Consumer
,以及一个ZooKeeper
集群,如下图所示。其中ZooKeeper
是Kafka
用来负责集群元数据的管理、控制器的选举等操作的。Producer
将消息发送到Broker
,Broker
负责将收到的消息存储到磁盘中,而Consumer
负责从Broker
订阅并消费消息。
1.4 Kafka的关键组件和概念
-
生产者(Producers):生产者是将消息发布到
Kafka
主题的应用程序。它们负责将消息发送到指定的主题,可以选择性地指定消息的键,以确保消息被发送到特定的分区。 -
Broker(代理):
Kafka
集群由多个Kafka
代理组成,每个代理都是一个Broker
。每个Broker
是一个独立的Kafka
服务器,负责存储和管理数据分区,以及处理生产者和消费者的请求。 -
主题(Topics):主题是
Kafka
中的基本数据组织单位。主题是消息的逻辑容器,生产者将消息发布到一个或多个主题,而消费者从主题中订阅消息。主题可以有多个分区,以便分布式存储和处理。 -
分区(Partitions):每个主题可以被分成多个分区,分区是
Kafka
中数据的物理存储单位。分区允许水平扩展和并行处理,每个分区都会在多个Broker
上复制,以提高容错性和可用性。一个分区只属于单个主题,很多时候也会把分区称为主题分区(Topic-Partition
)。同一主题下的不同分区包含的消息是不同的,分区在存储层面可以看作一个可追加的日志(Log
)文件,消息在被追加到分区日志文件的时候都会分配一个特定的偏移量(offset
)。offset
是消息在分区中的唯一标识,Kafka
通过它来保证消息在分区内的顺序性,不过offset
并不跨越分区,也就是说,Kafka保证的是单个分区有序而不是主题有序。Kafka中的分区可以分布在不同的服务器(broker)上,也就是说,一个主题可以横跨多个broker,以此来提供比单个broker更强大的性能。
每一条消息被发送到
broker
之前,会根据分区规则选择存储到哪个具体的分区。如果分区规则设定得合理,所有的消息都可以均匀地分配到不同的分区中。如果一个主题只对应一个文件,那么这个文件所在的机器I/O
将会成为这个主题的性能瓶颈,而分区解决了这个问题。 -
消费者(Consumers):消费者是从
Kafka
主题中订阅消息并处理它们的应用程序。消费者可以订阅一个或多个主题,并且可以从主题的不同分区中读取消息。消费者还可以组成消费者组,以实现负载均衡和容错性。 -
消费者组(Consumer Groups):多个消费者可以组成一个消费者组,每个组内的消费者共享对主题分区的消费。这有助于分布式消费,确保消息被处理一次且仅一次。
- 消费者组内每个消费者负责消费不同分区的数据,即一个组内的一个消费者只能消费一个分区中的消息;
- 如果一个消息想要被多个消费者消费,这些消费者必须在不同的消费者组;消费者组之间互不影响。
- 所有的消费者都属于某个消费者组,即消费者组是逻辑上的一个订阅者。
-
ZooKeeper:在早期版本的
Kafka
中,ZooKeeper
用于管理和维护Kafka
集群的元数据和领导者选举。但自Kafka 0.10
版本以后,Kafka
引入了自己的内部元数据存储,减少了对ZooKeeper
的依赖。 -
复制和容错性:
Kafka
通过在多个Broker
之间复制分区来提供容错性和高可用性。每个分区都有一个领导者(leader
)和多个副本(replica
)。领导者负责处理读写请求,而副本用于数据冗余和容错。 -
Replication(副本):同一个分区可能会有多个副本,同一分区的多个副本中保存的是相同的消息(在同一时刻,副本之间并非完全一样)。通过增加副本数量可以提升容灾能力。副本之间是“一主多从”的关系,其中leader副本负责处理读写请求,follower副本只负责与leader副本的消息同步。副本处于不同的
broker
中,当leader
副本出现故障时,从follower
副本中重新选举新的leader
副本对外提供服务。Kafka
通过多副本机制实现了故障的自动转移,当Kafka
集群中某个broker
失效时仍然能保证服务可用。 -
消息保留策略:
Kafka
允许配置消息在主题中的保留策略,以确定消息在主题中的保留时间。过期的消息将被自动删除,从而控制存储成本。 -
流处理:
Kafka
还支持与流处理框架(如Kafka Streams
和Apache Flink
)的集成,能够进行实时数据处理、转换和计算。
总结:
Kafka
的基本架构是一个分布式、高可用性的消息中间件系统,适用于处理实时数据流、构建事件驱动的应用程序以及构建大规模、可扩展的数据管道。它的设计使得能够处理大量数据并保持低延迟,使其在各种应用场景中得到广泛应用。
1.5 AR、ISR、OSR、HW、LEO
- AR(Assigned Replicas):
AR
意为 “分配的副本”,它表示每个分区有哪些Broker
上的副本。Kafka
的每个分区可以配置多个副本,AR
列出了分区的所有副本所在的Broker
。 - ISR(In-Sync Replicas):
ISR
表示 “同步副本”,它是AR
中的子集,指的是当前与主题分区的领导者(Leader
)保持同步的副本。这些副本收到了最新的消息,并且与领导者保持相对实时的数据同步。 - OSR(Out-of-Sync Replicas):
OSR
表示 “不同步副本”,它是AR
中除ISR
之外的副本。OSR
指的是不再与领导者保持同步的副本,可能由于网络问题或其他原因而滞后于领导者的数据。AR=ISR+OSR
。在正常情况下,所有的follower
副本都应该与leader
副本保持一定程度的同步,即AR=ISR
,OSR
集合为空。 - HW(High Watermark):表示分区中已经被提交(已被确认)的消息的位置。消费者只能消费到
HW
位置之前的消息,确保消息不会被重复消费。HW
也用于决定何时可以删除旧的消息。 - LEO(Log End Offset):
LEO
是分区中的最高偏移量,表示分区中已写入的最新消息的位置。LEO
是一个不断增加的值,代表了分区中的消息流的末尾。消费者可以读取LEO
之前的消息,但不能读取LEO
之后的消息。
这些概念在Kafka
中用于管理消息复制和消费进度。ISR
是一个特别重要的概念,因为它表示分区中的可靠副本,只有ISR
中的副本才会参与消息的复制和同步,确保消息不会丢失。HW
和LEO
用于帮助消费者确定何时可以安全地读取消息,以及何时可以删除旧的消息。AR
列表和ISR
列表是关于分区副本分布的元数据信息,对于Kafka
的分区管理和故障恢复非常重要。
注意!Kafka是消费端从服务端拉取消息
Kafka
消费端也具备一定的容灾能力。消费者使用拉(Pull
)模式从服务端拉取消息,并且保存消费的具体位置,当消费者宕机后恢复上线时可以根据之前保存的消费位置重新拉取需要的消息进行消费,这样就不会造成消息丢失。
1.6 kafka中的HW和LEO的区别
在Apache Kafka
中,HW(High Watermark)
和LEO(Log End Offset)
是两个重要的概念,用于跟踪和管理消息分发和消费的进度。它们之间的主要区别如下:
- High Watermark (HW - 高水位标记):
HW
是一个偏移量(offset
)的概念,表示消费者组中所有分区中已经提交的最高偏移量。HW
之前的所有消息都被认为是已经被成功消费的,因此不会再次发送给消费者。HW
可以看作是消息队列中已被消费的位置。- 消费者在确认(
commit
)消息时,会将其偏移量提交到HW
之前,表示它已经成功消费了这些消息。
- Log End Offset (LEO - 日志末尾偏移量):
LEO
是指每个分区中的日志中最新消息的偏移量,也就是分区中所有消息的末尾位置。LEO
表示分区中的所有消息,包括已经被消费的消息和尚未被消费的消息。- 消费者可以通过查询
LEO
来获取分区中的消息数量,以便了解还有多少消息等待被消费。
关键区别:
HW
是已提交的偏移量,表示已经成功被消费的消息的位置。LEO
是未提交的偏移量,表示分区中所有消息的末尾位置,包括未被消费的消息。- 消费者通常会维护每个分区的
HW
,以确保它们只消费HW
之前的消息。而LEO是用于管理分区的整体消息存储状态的指标。
在Kafka
中,HW
和LEO
是用来实现消息传递和消费的可靠性的关键组件。消费者组中的每个消费者会根据HW
来决定下一步消费哪些消息,确保消息不会被重复消费。同时,LEO
用于监控和管理消息的持久性,以便了解消息队列的状态。
1.7 kafka的复制机制
Kafka
作为一个分布式消息系统,其主要依赖数据复制机制来实现高可靠性和高性能。Kafka
的复制机制主要有以下几点:
- 数据复制基于主题(
Topic
)分区(Partition
)。每个分区可以配置多个副本(Replication
)。 - 一个分区的多个副本中,会选举出一个领导者(
Leader
),生产者和消费者只与Leader
交互。 - 其他的从副本(
Follower
)会从Leader
拉取数据,实时同步Leader
副本的消息。 - 当
Leader
副本失效时,其中一个Follower
会被自动选举为新的Leader
。 - 生产者每条消息会复制写入
Leader
副本和其Follower
副本,写入成功后才确认给生产者。 - 消费者只消费
Leader
副本的消息,如果Leader
失效,消费者会转移到新的Leader副本上。 - 每个消息会分配全局唯一的偏移量,用来保证消息消费的顺序性。
Kafka
依赖Zookeeper
来协调管理复制组信息,选举Leader
等。
依赖这种主题/分区+Leader/Follower
的复制机制,Kafka
可以支持大规模、高可靠的分布式消息传输。即使部分副本失效,也不会导致消息丢失。
1.8 Kafka如何保证消息不丢失
- 消息持久性:
Kafka
将所有消息持久存储在磁盘上,而不是在内存中,以确保即使在Kafka
代理(Broker
)发生故障时,消息也不会丢失。一旦消息被成功写入磁盘,它就被视为已提交。 - 消息复制:
Kafka
支持在多个broker
之间复制消息的副本。每个分区通常有一个主副本(Leader Replica
)和零个或多个追随副本(Follower Replicas
)。主副本负责接收和处理消息的写入请求,而追随副本追随主副本的数据变化。这种复制机制确保即使主副本发生故障,仍然可以从追随副本中获取消息,从而保证消息就不会丢失。 - ISR(In-Sync Replicas):只有与主副本保持同步的副本才能参与消息的复制和同步。如果一个副本落后于主副本太远,它将被从
ISR
中移除,直到追赶上来再重新加入。这确保了消息只有在多个副本之间达到一致性后才被视为已提交。 - 消息确认:
Kafka
提供了消息确认机制,生产者可以选择在消息被成功写入主副本后才收到确认,这样可以确保消息不会在传输过程中丢失。如果生产者在确认之前发生故障,它可以使用消息的副本来恢复。 - 消息复制因子:
Kafka
允许配置每个主题的消息复制因子(Replication Factor
),即每个分区的副本数。通常,至少需要配置一个以上的副本,以确保消息的冗余存储。 - 消息回溯:消费者可以根据偏移量重新读取历史消息。
- 消息保留策略:
Kafka
允许配置消息在主题中的保留策略,以确定消息在主题中的保留时间。过期的消息将被自动删除,从而控制存储成本。
总结:
Kafka
通过消息的持久性、复制、确认和复制因子等多重机制来确保消息不丢失。这些机制结合在一起,使Kafka
成为一个可靠的消息中间件,适用于处理重要业务数据和实时事件的场景。但请注意,消息的持久性和复制会增加一些存储和网络开销,因此需要权衡存储成本和可靠性要求。
1.8.1 生产者
- 启用消息重试:
Kafka
生产者通常配置了消息重试机制,如果消息发送失败(例如,Kafka
代理不可用),生产者会尝试多次重新发送消息。这可以确保消息在暂时的网络问题后仍然能够被成功发送到Kafka
。
retries=3 // 配置消息重试次数
-
使用同步发送:目前
Kafka
生产者是异步发送消息的,也就是说如果调用的是producer.send(msg)
这个API
,那么它通常会立即返回,但此时不保证消息发送已成功完成。可能会出现:网络抖动,导致消息压根就没有发送到Broker
端;或者消息本身不合规导致Broker
拒绝接收(比如消息太大了,超过了Broker
的限制)。Kafka
生产者可以选择使用同步发送模式,即在发送消息后等待确认(Acknowledgment
)。这意味着生产者会等待直到收到来自Kafka
代理的确认,以确保消息已成功写入分区的主副本。如果没有收到确认,生产者会尝试重新发送消息。这种方式可以确保消息不会在发送时丢失,但会增加延迟。 -
配置可靠性选项:
Kafka
生产者提供了多种可靠性选项,例如acks
参数,可以配置生产者要求的确认级别。acks
参数的取值可以是0、1或all,其中0表示不等待确认,1表示等待分区的主副本确认,all表示等待所有ISR
中的副本确认。选择适当的确认级别可以根据可靠性需求权衡延迟和性能。acks=all // 配置确认级别为所有 ISR 中的副本确认
1.8.2 消费者
- 偏移量(Offset)管理:
Kafka
消费者在消费消息时会跟踪维护每个分区的偏移量,表示已经成功消费的消息位置。消费者可以将偏移量提交到Kafka
,以便记录其消费进度。确保消费者正确管理和提交偏移量是防止重复消费的关键。 - 自动提交偏移量:
Kafka
消费者可以配置为自动提交偏移量,这意味着消费者将在后台定期自动提交已经成功处理的消息的偏移量。这种方式下,偏移量会在每次成功消费后自动增加,避免了重复消费。但是,自动提交偏移量时需要注意其频率,以免频繁提交偏移量。
enable.auto.commit=true
- 手动提交偏移量:消费者可以选择手动提交偏移量,这样就可以更精确地控制偏移量的提交时机。通常,在成功处理消息后,消费者会手动提交偏移量,以确保只有在消息被成功处理后才提交。
enable.auto.commit=false // 禁用自动提交偏移量
consumer.commitSync(); // 手动提交偏移量
- 消息去重:在某些情况下,可能需要在消费者端进行消息去重。这通常需要在消费者应用程序中实现自定义逻辑,以检查是否已经处理过某条消息。
1.9 Kafka如何保证消息不被重复消费
参考1:kafka概念详述
无论是数据丢失还是重新消费数据,其实本质还是因为offset
的问题,所以如果想要解决这些问题,则需要从这里下手,当时遇到这个问题,自己手动维护offset
。
每次消费一条数据,就更新每一个topic+partition
位置的offset
在内存中。当调用关闭consumer
线程的时候,把上面的偏移位置记录到文件中,下一次启动consumer
,从这里面读取offset
,然后使用consumer.seek()
方法指定到上次的offset
位置。
集群情况下,最好存储在redis
中。
1.10 Kafka如何保证消息有序
- 分区有序性:在
Kafka
中,每个主题(Topic
)可以被分为多个分区(Partitions
),每个分区内的消息是有序的,即消息按照其写入的顺序存储在分区中。这意味着,对于单个分区来说,消息的有序性是得到保证的。因此,如果消息的有序性对应用程序很重要,可以将相关消息发布到同一个分区中,以确保它们在消费时保持顺序。 - 分区级别的有序性:在
Kafka
中,不同分区之间的消息是并行处理的,因此跨分区的消息不一定保持全局有序性。如果需要全局有序性,需要确保所有相关的消息都发布到同一个分区中,或者在消费时在多个分区之间进行排序。 - 单一消费者:在每个分区内,一个消费者线程处理消息时,消息是有序的。如果应用程序只使用单个消费者来处理每个分区,那么在每个分区内可以维护有序性。
- 消费者组并行性:如果应用程序使用多个消费者组(
Consumer Group
)来并行处理消息,那么每个消费者组内的消费者线程会独立处理分区中的消息,这可能会导致消息的相对顺序在不同分区之间不一致。如果全局有序性对应用程序很重要,可以将所有分区分配给同一个消费者组,以确保消息在整个主题上保持有序性。 - 消息排序键(Key):在某些情况下,可以使用消息的键(
Key
)来确保有序性。消息的键决定了消息被分配到哪个分区,如果消息具有相同的键,它们将被分配到同一个分区中,并且在该分区内保持有序。
总结:
Kafka
提供了有序性的保证,但有序性是在分区级别的,并且取决于应用程序的设计和配置。如果全局有序性对应用程序很重要,需要谨慎设计和配置生产者和消费者,以确保消息在整个主题上保持有序性。在实际使用中,需要根据业务需求权衡性能和有序性。
1.11 kafka是如何根据消息key转发到指定分区
Kafka
使用以下步骤来根据消息键将消息转发到指定分区:
- 生成消息键:在生产者端,你可以选择为每条消息指定一个键(
message key
)。这个键通常是一个字符串、整数或其他可哈希的值。消息键的选择通常与应用程序的需求和数据的特性有关。 - 计算分区:
Kafka
使用消息键通过一个哈希函数或其他算法来计算出消息的哈希值。这个哈希值将决定消息应该被分发到哪个分区。通常,Kafka
使用一致性哈希算法来实现这一过程。 - 映射到分区:哈希值将被映射到一个特定的分区编号,该分区编号用于确定消息被发送到哪个分区。这确保了具有相同消息键的消息将始终被发送到相同的分区,从而保持了消息的顺序性。
- 发送消息:一旦确定了目标分区,生产者将消息发送到相应的分区。
Kafka
的分布式性质允许不同分区的消息并行处理,这有助于提高吞吐量。 - 消费消息:在消费者端,消费者会订阅一个或多个分区,并从这些分区接收消息。由于相同消息键的消息被发送到同一分区,因此消费者可以确保按照键的顺序接收消息。
总结:
Kafka
使用消息键来计算消息的哈希值,并将其映射到特定分区。这种方式有助于确保消息的有序性,并允许Kafka
在不同分区之间并行处理消息,以提高性能和可伸缩性。当需要分布式处理消息时,消息键成为了确保数据分发和负载均衡的关键因素。
1.12 Kafka的Partition和同一个消费组中消费者数量的关系
在Kafka
中,Partition
(分区)和同一个消费组中的消费者数量之间存在一定的关系,这个关系涉及到消息的分发和并行性。
- Partition数量大于消费者数量:如果一个
Kafka
主题有多个Partition
,而同一个消费组中的消费者数量比Partition
数量少,那么每个消费者可以订阅和处理多个Partition
。这样可以提高并行性,每个消费者可以并行地处理多个Partition
中的消息,从而提高整体的消费吞吐量。这种情况下,消息处理的顺序可能在不同Partition
之间不一致,但在同一个Partition
内仍然是有序的。 - Partition数量小于消费者数量:如果
Partition
数量比消费者数量多,那么一些消费者可能会处于空闲状态,因为每个Partition
只能由一个消费者处理。这种情况下,无法充分利用所有消费者的并行性,可能会导致资源浪费。但这可以用于某些场景下的负载均衡,其中需要确保每个消费者都处理相同数量的消息。 - Partition数量等于消费者数量:一种常见的最佳实践是尽量将
Partition
数量设置为等于消费者数量,这样每个消费者可以处理一个Partition
,确保消费者之间的负载均衡。这样做可以充分利用Kafka
的并行性,同时保持消息的有序性。这种配置方式通常能够达到较好的性能和有序性的平衡。
需要注意的是,如果将Partition
数量设置得过少,可能会限制整个消费者组的吞吐量,因为Kafka
无法提供足够的并行性。反之,如果Partition
数量设置得过多,可能会增加Kafka
集群的管理开销,因为每个Partition
都需要占用一定的资源。
因此,合适的Partition
和消费者数量的配置取决于具体的应用场景和性能需求。在实际情况中,可以进行性能测试和调优,以找到最适合的配置。不过,通常建议将Partition
数量设置为消费者数量的整数倍,以实现最佳的负载均衡和并行性
1.13 Partition数量少于customer数量时,为什么会造成customer闲置
当Kafka
主题的Partition
数量少于消费者(Consumer
)数量时,会导致一些消费者处于闲置状态的主要原因是因为Kafka
的分区粒度限制。每个Partition
只能由一个消费者组内的一个消费者线程来处理。这意味着多余的消费者将无法分配到活动的Partition
,从而变得闲置。
1.14 kafka的多分区,多副本如何实现容灾机制
- 多分区:每个
Kafka
主题可以分为多个分区(Partitions
)。分区允许消息在不同的物理存储上分布,从而提高了消息的并行性和处理能力。如果一个分区出现问题,其他分区仍然可以正常工作,确保了消息流的持续性。 - 多副本:每个分区可以配置多个副本(
Replicas
)。Kafka
采用多副本机制来提供容灾和高可用性。通常,每个分区至少有两个副本,一个是主副本(Leader Replica
),其余是追随副本(Follower Replicas
)。主副本负责接收和处理消息的写入请求,而追随副本追随主副本的数据变化。这样,即使主副本发生故障,追随副本可以升级为新的主副本,从而保持分区的可用性。 - ISR(In-Sync Replicas):
ISR
是一个与主副本同步的副本的集合。只有在ISR
中的副本才能参与消息的复制和同步。如果一个副本落后于主副本太远,它将被从ISR
中移除,直到追赶上来再重新加入。这确保了消息只有在多个副本之间达到一致性后才被视为已提交,从而提供了容灾性。 - 领导者选举:当主副本发生故障或不可用时,
Kafka
自动执行领导者选举,从ISR
中选择一个同步最快的追随副本升级为新的主副本,以确保分区的可用性。这个过程是自动的,无需人工干预。 - 多Broker分布:
Kafka
允许将不同分区的副本分布在不同的Kafka
代理(Brokers
)上,以提高整个Kafka
集群的容灾性。如果一个Kafka
代理发生故障,其他代理上的分区副本仍然可用,确保了消息的持续传输。
通过多分区和多副本的配置,Kafka
提供了强大的容灾机制,确保消息的可用性和可靠性。即使发生硬件故障、网络问题或其他意外情况,Kafka
仍然能够继续正常工作,确保不会丢失关键的消息数据。这使得Kafka
成为处理大规模数据和实时事件的理想选择。
1.15 Docker内查看Kafka数据
要在Docker
中查看Kafka
数据,可以按照以下步骤进行操作:
运行一个Kafka
消费者,连接到Kafka
集群,以便查看数据。可以使用Kafka
自带的命令行工具来执行此操作。例如:
docker exec -it <kafka_container_id> kafka-console-consumer.sh --bootstrap-server <kafka_broker> --topic <topic_name> --from-beginning
其中,<kafka_container_id
> 是Kafka
容器的ID,<kafka_broker
> 是Kafka
的Broker
地址,<topic_name
> 是想要查看的主题名称。
Docker
安装配置kafka-ui
工具
要在Docker
中安装和配置Kafka-UI
工具(用于管理和监控Kafka
集群),以下步骤:
- 拉取
Kafka-UI
镜像:
docker pull provectuslabs/kafka-ui
- 创建并运行
Kafka-UI
容器:
docker run -d --name kafka-ui -p 8080:8080 -e KAFKA_CLUSTERS_0_NAME=mykafka -e KAFKA_CLUSTERS_0_BOOTSTRAP_SERVERS=your_broker_host:9092 provectuslabs/kafka-ui
将your_broker_host
替换为实际的Kafka Broker
的主机名或IP
地址。
3. 访问 Kafka-UI:
在浏览器中访问:http://localhost:8080
(或根据在第2步中配置的端口号)。能够看到Kafka-UI
的管理界面。
1.16 Kafka的存盘的策略?
Apache Kafka使用一种基于日志(log)的存储模型来实现高性能的消息传递。Kafka的消息持久化主要依赖于两个概念:日志(Log)和分区(Partition)。下面是Kafka 中的存储策略:
- 日志(Log):
- Kafka的消息存储是以日志形式进行的,每个主题(Topic)都有一个或多个分区,每个分区都有一个对应的日志文件(Log File)。消息被追加到这个日志文件的末尾,形成一个有序的消息序列。因此,Kafka的存储模型是追加写入的,不会修改已经写入的消息。
- 分区(Partition):
- 每个主题可以划分为多个分区,每个分区都是一个有序的消息序列。分区的存在允许Kafka可以水平扩展,不同分区的消息可以独立地处理和存储,提高并行性。
- 消息的存储和删除策略:
- Kafka的消息默认是异步刷盘的,即消息首先写入到内存中的page cache,然后以异步的方式刷写到磁盘。这样可以提高写入性能。Kafka使用的是操作系统的文件系统来进行持久化存储。
- Kafka提供了消息的保留策略,可以通过配置参数来控制消息在分区中的保留时间和大小。消息可以达到指定的保留时间或保留大小后被删除。这样可以防止日志文件无限增长,控制存储的成本。
- 复制和持久性:
- Kafka支持分布式部署,每个分区的数据都可以有多个副本(Replica)存储在不同的节点上,以实现数据的冗余备份。这提供了高可用性和容错性。当Leader副本接收到消息后,会将消息同步到其他副本,只有当所有副本都确认收到消息时,Leader才会认为消息已经被成功提交。
- 通过配置参数,可以设置每个分区的复制因子(Replication Factor),即一个分区的副本数。增加复制因子可以提高数据的冗余备份程度,但也会增加写入的延迟。
总体来说,Kafka的存储策略是基于分区和日志的追加写入模型,结合异步刷盘、分区副本、消息保留策略等机制,提供了高性能、高可用性和可扩展性。这使得Kafka在处理大规模数据流和构建实时数据管道方面非常有效。
1.17 有遇到过消费不均的情况吗?就可能多个消费分区,然后有些分区的量很多,有些消费分区的量那不太多的这种情况。
优化:Kafka有些分区数据量大,有些分区数据量小,消费不均怎么处理
Kafka的分区数据量不均匀的情况是比较常见的,这可能是由于生产者写入数据的不均匀、分区数量不合理、某些分区的消息过期而被删除等原因导致的。为了处理这种情况,可以采取以下一些策略:
- 重新分区:如果分区数据量差异较大,可以考虑重新规划分区。增加或减少分区的数量,并根据数据负载情况重新分配分区。这个操作可能需要谨慎处理,因为改变分区数量会影响到消费者的消费进度,可能导致一些消息的重复或漏掉。
- 消费者负载均衡:如果有多个消费者组,确保它们能够均匀地消费各个分区。Kafka消费者组的成员会根据分区数量动态分配分区。如果某个消费者组的某个消费者处理的分区数据较多,其他消费者则会接收到相对较少的分区。这样可以通过增加或减少消费者的数量来实现负载均衡。
- 动态调整分区数量:Kafka 0.11版本及更高版本引入了动态调整分区数量的能力。通过增加或减少分区数量,可以更好地适应数据的变化。注意,这个操作也需要谨慎处理,因为增加分区会导致重新分配数据,可能影响到消费者的进度。
- 手动分配分区:在某些情况下,可以手动为每个消费者分配特定的分区,以确保每个消费者处理的分区数量相对均匀。这样的分区分配方式可以通过在消费者配置中设置partition.assignment.strategy来实现。
需要注意的是,动态调整分区数量和手动分配分区都需要小心处理,因为它们可能导致一些数据的重复或漏掉,对于生产环境的操作建议谨慎使用,并在非生产环境中进行测试验证。在大多数情况下,适当调整分区数量和使用Kafka提供的自动负载均衡机制就可以处理数据不均匀的情况。
2 RabbitMQ
RabbitMQ
是一个开源的消息中间件,它实现了高级消息队列协议(Advanced Message Queuing Protocol,AMQP
),用于在分布式系统中传递消息。RabbitMQ
提供了一种可靠的、高度可扩展的消息传递机制,使不同组件之间可以异步地通信,从而构建分布式和松耦合的应用程序。
2.1 RabbitMQ和Kafka的区别
- 数据流模型:
- RabbitMQ:
RabbitMQ
是一个传统的消息队列系统,它强调消息的可靠性传递。消息发送者将消息发送到队列,消息接收者从队列中获取消息,并且可以确保消息被成功处理。RabbitMQ
更适合点对点通信和发布/订阅模型。 - Kafka:
Kafka
是一个分布式流数据平台,它以事件日志的方式存储消息,并允许多个消费者以不同的速度和顺序访问消息流。Kafka
更适合处理大量实时事件流,例如日志收集、监控、事件溯源等场景。
- RabbitMQ:
- 数据保留期限:
- RabbitMQ:
RabbitMQ
默认情况下会保留消息,直到它们被消费或过期。它通常用于传递实时数据,不会持久化大量历史数据。 - Kafka:
Kafka
是一个持久性的数据存储系统,消息通常会被保存一段时间,以便后续分析和处理。这使得Kafka
适用于构建数据湖(data lake
)和事件溯源系统。
- RabbitMQ:
- 消费者灵活性:
- RabbitMQ:
RabbitMQ
的消息通常只能由一个消费者处理,多个消费者可以竞争获取消息,但只有一个消费者会成功。这适用于任务分配和负载均衡。 - Kafka:
Kafka
支持多个消费者组,每个组都可以独立消费相同的消息流。这允许多个消费者以不同的方式处理相同的消息数据,使其更适合用于日志处理和事件处理。
- RabbitMQ:
- 扩展性:
- RabbitMQ:
RabbitMQ
的扩展性主要通过集群实现,可以在多个节点上运行以提高可用性。但它通常需要垂直扩展来处理更多的消息吞吐量。 - Kafka:
Kafka
的扩展性非常强大,可以水平扩展以处理高吞吐量的数据流。它分布式存储消息,分区和副本机制使得它能够轻松应对大规模数据流。
- RabbitMQ:
- 适用场景:
- RabbitMQ:适合于传统的消息队列需求,如任务队列、
RPC
(远程过程调用)、通知和分布式系统的点对点通信。 - Kafka:适用于大规模数据流处理,如实时数据管道、日志和事件流处理、事件溯源、大数据分析等。
- RabbitMQ:适合于传统的消息队列需求,如任务队列、
总结:
RabbitMQ
和Kafka
都是强大的消息中间件工具,但它们在数据模型、保留期、消费者灵活性、扩展性和适用场景方面有不同的优势。选择哪个取决于具体需求和架构设计。有时候也可以将它们结合使用,以满足不同层次和类型的消息传递需求。
2.2 RabbitMQ的消息确认和Kafka的ack是一样的吗
RabbitMQ的消息确认:
在RabbitMQ
中,消息确认是指消费者向RabbitMQ服务器发送确认消息
,以告知服务器已成功处理了一条或多条消息。RabbitMQ
使用 "ACK"(acknowledgment)
机制来实现消息确认。
RabbitMQ
消息确认的一些关键点:
- 手动确认:
RabbitMQ
支持手动消息确认,这意味着消费者需要明确地发送ACK
消息来确认已经处理了消息。这确保了消息不会在处理失败时丢失,但也需要谨慎处理ACK
。 - 自动确认:
RabbitMQ
也支持自动确认,这时服务器会在消息被传递给消费者后立即将消息标记为已确认。这通常用于不需要严格保证消息传递的场景。 - 消息重传:如果消费者在处理消息时发生错误,并且使用手动确认模式,
RabbitMQ
可以将消息重新发送给另一个消费者,以确保消息被处理。
Kafka的ack(确认)机制:
在Kafka
中,消息确认的概念稍有不同。Kafka
使用了一种名为"ack"
的机制,用于控制消息的复制和提交。以下是Kafka ack
的关键点:
- 生产者ack:
Kafka
生产者可以配置ack
级别,以确定何时将消息视为成功发布。可选的ack
级别包括:- “acks=0”:生产者不等待任何确认,将消息发送到代理即可。
- “acks=1”:生产者等待领导者代理确认消息写入日志后,即视为成功发布。
- “acks=all”(或 “acks=-1”):生产者等待所有副本都确认消息写入日志后,才视为成功发布。
- 消费者offset: 在
Kafka
中,消费者控制其消费的位置(offset
)。消费者会定期提交已处理消息的offset
,以标记它们已经成功消费。Kafka
可以根据offset
确保消息不会重复处理。
总结:
尽管RabbitMQ
和Kafka
都使用消息确认或ack
的概念,但它们的具体实现和含义略有不同。在RabbitMQ
中,消息确认更多地涉及到确认消息的接收,而在Kafka
中,ack
主要用于控制消息的生产和提交。这些差异是由它们不同的设计和用途所决定的。
2.3 为什么选择RabbitMQ而不是Kafka
选择RabbitMQ
而不是Kafka
的理由:
- 需要严格的消息可靠性:如果应用程序对消息的可靠性有非常高的要求,例如不允许消息丢失,
RabbitMQ
可能更适合。RabbitMQ提供了强大的消息持久性和消息确认机制,确保消息在发送和接收过程中不会丢失。 - 点对点通信:如果应用程序主要是点对点通信或任务队列的模式,
RabbitMQ
更适合。RabbitMQ
的消息队列模型适用于将任务分配给多个消费者,确保每个任务只被处理一次。 - 更易于部署和管理:
RabbitMQ
的配置和管理相对较简单,特别是对于小型到中型规模的部署。它更容易上手,因为不需要配置复杂的分区、副本和其他高级设置。 - 更传统的消息队列:如果已经熟悉传统消息队列模型,例如
AMQP(RabbitMQ的协议)
,并且希望继续使用这种模型,那么RabbitMQ
是一个合适的选择。 - 集成易用性:
RabbitMQ
有许多客户端库和插件,可以轻松地与各种编程语言和应用程序集成,这使得它在某些情况下更易于使用。
选择Kafka
而不是RabbitMQ
的理由:
- 大规模数据流处理:如果需要处理大规模实时数据流,如日志收集、事件溯源、实时分析等,
Kafka
的分布式、高吞吐量特性更适合这些场景。 - 消息保留和历史数据:如果需要长时间保留消息或构建数据湖(
data lake
)来进行后续分析,Kafka
的持久性和数据保留能力更强。 - 多个消费者组:如果需要多个独立的消费者组以不同的方式处理相同的消息流,
Kafka
提供了更灵活的消费者组管理。 - 高度可扩展性:如果预计需要构建高度可扩展的消息处理系统,
Kafka
的分区和副本机制使其更适合应对大规模和高可用性要求。 - 大量的事件数据:如果应用程序生成或消费大量的事件数据,
Kafka
的事件日志模型可能更适合。
2.4 RabbitMQ特点
- 多种消息传递模式:
RabbitMQ
支持多种消息传递模式,包括点对点(P2P
)通信和发布/订阅模式。这使得它适用于各种应用场景,从任务分发到事件驱动架构。 - 消息持久性:
RabbitMQ
允许将消息和队列设置为持久性,以确保它们在服务器重启后仍然存在。这对于关键的业务消息非常重要,因为它们不会在系统故障时丢失。 - 消息确认机制:
RabbitMQ
支持消息确认机制,允许消费者发送确认(acknowledgment
)以告知服务器已成功处理消息。这确保了消息不会重复传递给同一消费者。 - 灵活的消息路由:
RabbitMQ
提供了不同类型的交换机(Exchange
),包括直接交换机、扇出交换机、主题交换机和头交换机,以支持不同的消息路由策略。 - 死信队列:
RabbitMQ
支持死信队列,可用于处理无法被消费的消息,以及实现消息重试机制。 - 多语言客户端:
RabbitMQ
提供了多种编程语言的客户端库,使得开发者可以轻松与RabbitMQ
进行集成。 - 可扩展性:
RabbitMQ
支持集群配置,允许多个节点一起工作,以提高可用性和处理更多消息的能力。 - 插件系统:
RabbitMQ
的插件系统允许扩展和自定义其功能,以满足不同应用程序的需求。 - 管理界面:
RabbitMQ
提供了一个易于使用的Web
管理界面,用于监控和管理队列、交换机和连接。 - 优先级:
RabbitMQ
允许消息设置优先级,确保高优先级消息在队列中得到更快的处理。 - 灵活的消息过滤:
RabbitMQ
提供了基于消息属性和头部的灵活消息过滤功能,允许消费者选择性地订阅特定消息。
总结:
RabbitMQ
是一个强大、灵活且可靠的消息中间件,适用于各种分布式应用和消息传递场景。它的特点包括消息队列模型、多种消息传递模式、消息持久性、消息确认机制、多语言客户端、可扩展性等,使得它成为一个广泛应用于企业和开源项目中的消息中间件解决方案。
2.5 RabbitMQ使用场景
RabbitMQ
是一种灵活的消息中间件,适用于各种不同的使用场景和应用程序,特别是那些需要可靠的消息传递和解耦的情况。以下是一些RabbitMQ
的常见使用场景:
- 任务队列:
RabbitMQ
可用于创建任务队列,其中生产者将待处理的任务发布到队列,而多个消费者可以异步地从队列中获取任务并处理它们。这适用于异步处理和任务调度。 - 分布式系统通信:
RabbitMQ
提供了分布式系统组件之间的可靠通信方式,可以在不同部分之间传递消息,从而实现系统解耦和松耦合。 - 发布/订阅模式:
RabbitMQ
支持发布/订阅模式,其中生产者发布消息到交换机,而多个消费者可以订阅该交换机并接收消息。这在事件驱动架构中很有用。 - 日志收集:
RabbitMQ
可用于日志收集系统,其中日志消息从各个应用程序发送到消息队列,然后由消费者处理和存储。这有助于集中化和实时监控日志。 - 通知系统:
RabbitMQ
可用于构建通知系统,其中系统的各个部分可以发布通知消息,而感兴趣的消费者可以订阅这些通知消息以接收及时的信息更新。 - 订单处理: 在电子商务应用中,
RabbitMQ
可用于处理订单和库存更新。当订单创建或状态更改时,相关消息可以通过RabbitMQ
传递给不同的系统组件来执行相关操作。 - 微服务架构:
RabbitMQ
可用于微服务架构中的服务间通信。微服务可以使用消息队列来协调和通信,确保松耦合和高可用性。 - 负载均衡:
RabbitMQ
可以用作负载均衡器,将消息分发到多个消费者以实现并行处理。 - 缓存更新:
RabbitMQ
可用于实时缓存更新,当数据发生变化时,将更新消息发送到缓存系统,以确保数据的一致性。 - 实时监控和报警:
RabbitMQ
可用于实时监控和报警系统,用于收集系统指标和报警事件,从而及时采取行动。 - 数据同步:
RabbitMQ
可用于数据同步和复制,确保不同数据存储之间的数据一致性。
总结:
RabbitMQ
是一种功能强大且灵活的消息中间件,适用于各种不同的应用场景,包括任务队列、事件驱动架构、分布式系统通信、日志收集、微服务架构等等。选择RabbitMQ
的合适使用场景通常取决于具体需求和架构设计。
2.6 RabbitMQ核心概念
-
消息(Message):消息是
RabbitMQ
中的基本数据单元。它是需要传递的信息,可以是任何数据,通常是以字节流的形式存在。由Properties
和Body
组成,Properties
可以对消息进行修饰,比如消息的优先级、延迟等高级特性;Body
则就是消息体内容。 -
生产者(Producer):生产者是负责发布消息到
RabbitMQ
的应用程序或组件。生产者将消息发送到一个交换机。 -
交换机(Exchange):交换机是消息的路由器,它决定消息应该被发送到哪个队列。
RabbitMQ
提供了不同类型的交换机,包括直接交换机、扇出交换机、主题交换机和头交换机,以支持不同的消息路由策略。 -
队列(Queue):队列是消息的缓冲区,它们用于存储消息,等待被消费者获取和处理。每个队列都有一个名称,消费者可以订阅特定队列以接收消息。一个消息可投入一个或多个队列。消息一直在队列里面,等待消费者连接到这个队列将其取走。
-
消费者(Consumer):消费者是应用程序或组件,负责从队列中获取消息并处理它们。消费者可以订阅一个或多个队列,以便在消息到达时触发回调函数来处理消息。
-
消息确认(Acknowledgment):消息确认是指消费者向
RabbitMQ
服务器发送确认消息,以告知服务器已成功处理了一条或多条消息。这确保了消息不会在处理失败时丢失。 -
持久性(Durability):持久性是一种设置,它确保交换机、队列和消息在服务器重启后保留。持久性可以防止消息丢失,确保数据的可靠性。
-
交换机绑定(Exchange Binding):交换机绑定是一种配置,它将交换机与队列关联起来,以定义如何将消息路由到队列。
exchange
根据这个关键字进行消息投递。 -
路由键(Routing Key):路由键是一个消息属性,用于决定交换机将消息路由到哪个队列。路由键的值由生产者设置,并且与交换机的绑定规则相匹配。
-
死信队列(Dead Letter Queue):死信队列是用于处理无法被消费者处理的消息的队列,通常用于消息重试和错误处理。
-
虚拟主机(Virtual Host):虚拟主机是
RabbitMQ
中用于进行逻辑隔离,最上层的消息路由。它允许将多个应用程序隔离在单独的环境中,以避免冲突和混淆。一个Virtual Host
里面可以有若干个Exchange
和Queue
,同一个Virtual Host
里面不能有相同名称的Exchange
或Queue
。每个
vhost
本质上就是一个mini
版的RabbitMQ
服务器,拥有自己的队列、交换器、绑定和权限机制。vhost
是AMQP
概念的基础,必须在连接时指定,RabbitMQ
默认的vhost
是/
。 -
连接(Connection):连接是生产者和消费者与
RabbitMQ
服务器之间的网络连接。每个连接可以包含一个或多个通道。 -
通道(Channel):网络信道,几乎所有的操作都在
Channel
中进行,Channel
是进行消息读写的通道。客户端可建立多个Channel
,每个Channel
代表一个会话任务。也就是说,一般情况是程序起始建立TCP
连接,第二步就是建立这个Channel
。 -
集群(Cluster):
RabbitMQ
支持集群配置,多个节点可以一起工作以提高可用性和消息传递的吞吐量。 -
插件(Plugin):
RabbitMQ
的插件系统允许扩展和自定义其功能,以满足不同应用程序的需求。
2.7 Exchange说明
在RabbitMQ
消息中间件中,Exchange
(交换机)是消息路由的关键组件之一。Exchange
决定了消息应该被发送到哪个队列。RabbitMQ
提供了不同类型的Exchange
以满足不同的消息路由需求。
Exchange
的作用:Exchange
接收生产者发送的消息,并根据特定的规则将消息路由到一个或多个队列中。生产者将消息发送到Exchange
,而不是直接发送到队列,因此Exchange
充当了消息的路由器。Exchange
的类型:RabbitMQ
提供了四种主要类型的Exchange
,包括:- Headers Exchange(头交换机):根据消息的头部属性进行匹配,而不是路由键。可以定义一组键值对来匹配消息。
headers
交换器和direct
交换器完全一致,但性能差很多,目前几乎用不到了。 - Direct Exchange(直接交换机):它会把消息路由到那些
binding key
与routing key
完全匹配的Queue
中,不匹配的则不会转发,它是单播的模式。【一对一】 - Fanout Exchange(扇出交换机):将消息广播到所有与之绑定的队列,忽略路由键,广播模式。这用于发布/订阅模式。
fanout
类型转发消息是最快的。 - Topic Exchange(主题交换机):将消息与通配符形式的路由键进行匹配,前面讲到
direct
类型的Exchange
路由规则是完全匹配binding key
与routing key
,但这种严格的匹配方式在很多情况下不能满足实际业务需求。topic
类型的Exchange
在匹配规则上进行了扩展,它与direct
类型的Exchage
相似,也是将消息路由到binding key
与routing key
相匹配的Queue
中,但这里的匹配规则有些不同,它约定:routing key
为一个句点号“. ”分隔的字符串(我们将被句点号“. ”分隔开的每一段独立的字符串称为一个单词),如“stock.usd.nyse”、“nyse.vmw”、“quick.orange.rabbit”。binding key
与routing key
一样也是句点号“. ”分隔的字符串。binding key
中可以存在两种特殊字符“*
”与“#
”,用于做模糊匹配,其中“*
”用于匹配一个单词,“#
”用于匹配多个单词(可以是零个)
- Headers Exchange(头交换机):根据消息的头部属性进行匹配,而不是路由键。可以定义一组键值对来匹配消息。
- Exchange的绑定(Binding): 生产者和队列之间的关联通过
Exchange
和Binding
来建立。Binding
是Exchange
和队列之间的关系,它指定了消息如何从Exchange
路由到与之绑定的队列。 - 默认Exchange:
RabbitMQ
还提供一个默认Exchange
,它是一个直接交换机,不需要额外的配置。它的名称是空字符串("")
或者amq.default
。当生产者发送消息到默认Exchange
时,会根据消息的路由键将消息直接路由到队列,路由键和队列的名称必须完全匹配。 - 未匹配的消息: 如果消息无法路由到任何队列,
RabbitMQ
可以将它们进行丢弃、返回给生产者或者发送到死信队列。 - Exchange的持久性:
Exchange
可以被设置为持久性,以确保在RabbitMQ
服务器重启后仍然存在。这对于关键的Exchange
非常重要,因为它们确保了消息的可靠路由。
Exchange
是RabbitMQ
消息路由的核心,根据Exchange
的类型和绑定规则,可以实现不同的消息路由策略,满足各种不同的消息传递需求。选择合适的Exchange
类型和设置正确的绑定规则是构建可靠消息传递系统的重要一步。
2.8 RabbitMQ的消息是基于什么传输
RabbitMQ
消息中间件基于一种称为AMQP(Advanced Message Queuing Protocol,高级消息队列协议)
的网络传输协议。AMQP
是一种开放的、标准化的消息传递协议,设计用于实现高效、可靠和跨平台的消息传递。
一些关于AMQP
的重要信息包括:
- 协议标准:
AMQP
是一个开放的、协议化的标准,由多个厂商和组织制定和维护。这意味着任何实现了AMQP
标准的消息中间件都可以与其他实现了相同协议的系统进行通信。 - 多语言支持:
AMQP
协议支持多种编程语言和平台,因此可以实现不同语言的客户端库,使得不同的应用程序可以轻松地与RabbitMQ
或其他AMQP
兼容的消息中间件进行交互。 - 可靠性和消息确认:
AMQP
提供了消息可靠性和消息确认机制,确保消息在发送和接收时不会丢失。这对于保证消息传递的可靠性至关重要。 - 灵活性:
AMQP
协议非常灵活,允许定义不同类型的交换机、队列和消息路由规则,以满足不同的消息传递需求。 - 异步通信:
AMQP
支持异步通信模式,使得生产者和消费者可以异步地发送和接收消息,从而构建分布式和松耦合的系统。
总结:
RabbitMQ
是一个实现了AMQP
协议的消息中间件,它使用AMQP
协议来实现消息的可靠传递和分发。这使得RabbitMQ
能够在各种不同的应用程序和编程语言之间实现可扩展和可靠的消息传递。
2.9 RabbitMQ如何保证消息不丢失、不重复消费
保证消息不丢失的机制:
- 持久性(Message Durability):在发布消息时,可以将消息标记为持久性。这意味着即使
RabbitMQ
服务器在消息发布后崩溃,消息也不会丢失。持久性消息会被存储在磁盘上,直到它们被成功传递给消费者。 - 持久性队列(Queue Durability):队列也可以被标记为持久性。这确保了即使
RabbitMQ
服务器崩溃,队列的定义和其中的消息也会在服务器重新启动后恢复。 - 消息确认(Message Acknowledgment):消费者在成功处理消息后可以发送确认消息(
ACK
)给RabbitMQ
服务器。只有在消费者确认收到消息并成功处理后,RabbitMQ
才将消息从队列中删除,否则消息会保持在队列中等待重新传递。
保证消息不重复消费的机制:
- 消息幂等性(Message Idempotence):消费者应该设计成具有幂等性,这意味着多次处理相同消息不会产生不同的效果。这可以通过在消息处理过程中使用唯一标识符来实现,以确保重复处理相同消息不会导致不一致的结果。
- 唯一消息标识符(Message Deduplication):消费者可以维护一个记录已经处理过的消息的列表,并在处理新消息之前检查消息的唯一标识符。这可以防止重复处理相同的消息。
- 幂等性操作和事务(Idempotent Operations and Transactions):如果消费者需要执行多个操作来处理消息,应该使用事务或者设计这些操作为幂等的,以确保在消息重试时不会引发不一致的状态。
- 消费者端的去重(De-duplication on the Consumer Side):消费者可以自行实现去重逻辑,将已经处理过的消息记录在外部存储中,并在处理新消息之前检查记录,以防止重复消费。
需要注意的是,虽然RabbitMQ
提供了一些机制来确保消息不丢失和不重复消费,但要实现完全的消息可靠性,还需要根据具体应用的需求和场景来合理设计消息处理逻辑,并考虑各种可能的故障情况。这可能涉及到持久性、确认、幂等性和事务等多个方面的处理。
2.10 RabbitMQ如何保证消息的顺序性
RabbitMQ
本身是一个多线程、分布式的消息中间件,它不保证消息的绝对顺序性。然而,可以采取一些策略和配置来尽量保证消息的相对顺序性:
- 单一队列:将所有相关的消息发送到同一个队列。这确保了这些消息在队列中的顺序性。多个消费者可以从同一个队列中获取消息,并以先到先得的顺序处理消息。
- 单一消费者:如果消息的绝对顺序性对于应用非常重要,可以考虑使用单一的消费者来处理这个队列中的消息。这样可以确保消息按照它们到达队列的顺序进行处理。
- 消息分区:如果需要同时处理多个相关的消息流,可以将它们发送到不同的队列中,然后在消费端进行协调以确保顺序性。例如,可以将消息按照某种规则分发到多个队列,并确保每个队列中的消息顺序被维护。
- 队列优先级:
RabbitMQ
允许为队列设置优先级,以确保高优先级的消息在队列中得到更早的处理。这可以在需要时用于确保某些消息的顺序性。 - 消息的标识符:消息本身可以包含一个唯一的标识符或序列号,消费者可以根据这些标识符来确定消息的顺序,并进行相应的处理。
需要注意的是,RabbitMQ
是一个并行分布式系统,它不能保证消息的绝对顺序性。在高负载或故障恢复情况下,消息的顺序可能会被打破。因此,在设计应用程序时,应该谨慎考虑消息的顺序性需求,以便在可能的情况下采取适当的措施来处理这些需求。如果消息的绝对顺序性对于应用非常关键,可能需要考虑其他消息中间件或架构来更好地满足这些需求。
2.11 RabbitMQ生产的时候有几种模式
- 简单队列(Simple Queue):最基本的模型,生产者将消息发布到队列,消费者从队列头部消费消息。
- 工作队列(Work Queue):用于任务分发,生产者发布任务到队列,多个消费者同时监听队列,
Broker
依次将任务分发给消费者执行。 - 发布/订阅(Publish/Subscribe):生产者将消息发布到
topic
,多个订阅了该topic
的消费者都可以收到消息。 - 路由(Routing):生产者将消息发布到
Exchange
,Exchange
根据routing key
将消息路由到不同的Queue
,消费者监听对应的Queue
获取消息。 - 主题(Topics):生产者将消息发布到
Exchange
,Exchange
根据routing pattern
(通配符方式的routing key
)将消息路由到不同的Queue
。 - RPC请求-响应(RPC Request-Reply):生产者向
RPC Queue
发布请求消息,消费者监听队列处理请求并产生响应,生产者接收响应结果。
2.12 RabbitMQ的消费者在经历了网络闪断之后还能继续消费,还是说需要服务重启后才能再消费
具体描述:RabbitMQ
的消费端,作为消费端在去启动应用之后,有些消费者去对topic进行消费,当应用的网络闪断之后,应用中的消费者在网络恢复后还能继续消费吗,还是说需要应用重启后才能再消费
RabbitMQ
的消费者在经历了短期的网络闪断或连接丢失后通常可以继续消费,前提是以下条件得到满足:
- 持久化消息:如果消息是持久化的,即在发布时将消息标记为持久化,那么即使在网络闪断期间,消息也会在
RabbitMQ
服务器上保持不变。当消费者重新连接时,可以继续消费这些消息。 - 消息确认(Acknowledgment):如果消费者在处理消息时发送了确认消息(
ACK
),则消息将被标记为已经处理。即使在断开连接后重新连接,RabbitMQ
也不会再次发送这些已经被确认的消息给消费者。 - 自动重连机制:消费者库通常会实现自动重连机制,以便在网络连接断开后尝试重新连接到
RabbitMQ
服务器。这有助于确保消费者可以在网络恢复后继续消费消息。 - 手动重试:如果消费者无法处理消息或需要重新处理某些消息,可以在消息处理失败后手动重新发布或重新入队消息。这样,消息将再次进入队列,等待被消费者重新处理。
需要注意的是,网络闪断期间,消费者可能无法及时处理消息,这可能导致一些延迟。为了处理这种情况,可以根据应用程序的需求设计相应的重试和恢复机制,以确保消息在消费者重新连接后能够得到处理。
总结:
RabbitMQ
的消费者通常可以在经历网络闪断后继续消费消息,前提是消息被持久化,并且消费者具有自动重连或手动重试机制。这使得 RabbitMQ
成为可靠的消息中间件,适用于需要保持消息可靠性的应用场景。
2.12.1 RabbitMQ的消费者在读取完数据后,并没有返回ack,此时消息处于什么状态
RabbitMQ
消息的两种状态:
- Ready:消息等待消费。
- Unacked:消息已被消费者取走,但消费者还未返回确认消费。
当RabbitMQ
消息中间件的消费者读取完数据后,但尚未发送确认消息(ACK
),消息处于未确认(Unacknowledged
)状态。在这种状态下,RabbitMQ
认为消息尚未被成功处理,并将保留这条消息以等待进一步的处理或确认。
未确认状态的消息仍然在队列中,并可以被其他消费者(如果有多个消费者)或同一个消费者的不同实例重新消费,直到它被确认为止。这种机制有助于确保消息不会在处理期间丢失,即使消费者在处理期间发生故障或需要更多时间来处理消息。
一旦消费者发送确认消息(ACK
),RabbitMQ
将从队列中删除该消息,表示消息已经被成功处理。如果消费者在处理消息期间发生错误或决定不再处理消息,可以发送拒绝消息(NACK
)或拒绝该消息并重新入队(Reject and Requeue
),以触发不同的处理行为。
未确认消息的处理方式取决于消费者的逻辑和策略。消费者可以在成功处理消息后发送ACK
,或者根据业务逻辑发送NACK
或拒绝消息。在设计应用程序时,需要考虑如何处理未确认消息以确保消息的可靠传递和可靠性。
2.12.2 RabbitMQ的消息在上一个消费者没有返回ack后,还能被其他消费者消费吗
可以,RabbitMQ
允许在上一个消费者没有返回确认消息(ACK
)之前,将同一条消息传递给其他消费者。这是RabbitMQ
的消息传递模型的关键特性之一,允许多个消费者并发地处理消息。
具体来说,当一条消息发送给多个消费者时,每个消费者都可以尝试处理该消息。只要一个消费者返回了ACK
,RabbitMQ
就会从队列中删除消息,表示消息已经被成功处理。其他消费者仍然可以尝试处理相同的消息,但它们将会继续竞争并试图发送ACK
。
这种机制允许实现负载均衡和并行处理。如果有多个消费者,并且希望它们并发地处理消息以提高处理速度,可以使用这种特性。但要注意,如果消费者处理消息的速度非常慢或发生错误,可能会导致消息在队列中积压,因此需要谨慎考虑消费者的性能和错误处理机制,以确保系统的稳定性和性能。
2.13 RabbitMQ集群
2.13.1 RabbitMQ如何保证稳定性以及灾备能力
- 消息持久性(Message Durability):生产者在发布消息时可以将消息标记为持久性,这意味着消息会被保存在磁盘上,即使
RabbitMQ
服务器在消息发布后崩溃,消息也不会丢失。消费者在从队列中获取消息时也应该发送确认消息(ACK
),以确保消息被成功处理。 - 队列持久性(Queue Durability):队列也可以被标记为持久性,这确保了队列的定义和其中的消息在
RabbitMQ
服务器重启后会被恢复。这对于保证队列的可用性和数据的完整性非常重要。 - 镜像队列(Mirrored Queues):
RabbitMQ
支持镜像队列,允许将队列的数据复制到多个节点,以提高可用性和容错性。这意味着即使一个节点失败,其他节点上的镜像队列仍然可以继续提供服务。 - 集群配置(Clustering):
RabbitMQ
支持集群配置,多个RabbitMQ
节点可以一起工作,共同组成一个集群。这提高了消息中间件的可用性和吞吐量,并提供了灾备能力。如果一个节点失败,集群中的其他节点可以继续处理消息。 - 备份交换机(Alternate Exchanges):
RabbitMQ
支持备份交换机,它允许将未能路由的消息发送到备份交换机,以防止消息丢失。这提供了一种容错机制,确保即使某些消息无法路由,它们也会得到处理。 - 故障恢复(Failover):
RabbitMQ
客户端库通常支持故障恢复功能,能够自动检测到RabbitMQ
节点的故障并切换到另一个可用节点,从而提高应用程序的可用性。 - 备份和数据恢复策略:定期备份
RabbitMQ
数据以防止数据丢失,并建立有效的数据恢复策略,以应对不同类型的故障情况。 - 监控和警报:部署监控系统以实时监测
RabbitMQ
服务器的性能和状态,并设置警报以在发生问题时及时采取行动。 - 定期维护和升级:定期进行
RabbitMQ
的维护和升级,以确保系统的稳定性和安全性。
综合利用这些策略和机制,可以建立一个高可用性、可靠性和灾备能力强的RabbitMQ
消息中间件环境,确保消息的安全传递和系统的稳定性。不过,要根据具体的需求和复杂性来考虑配置和管理。
2.13.2 RabbitMQ集群的搭建方案
参考1:手把手教你搭建 RabbitMQ 集群
参考2:RabbitMQ 如何保证高可用的?
RabbitMQ
集群的两种模式:
- 普通集群:无法实现高可用
- 镜像集群:能实现高可用
普通集群模式
普通集群模式是在多台机器上启动多个RabbitMQ
实例,每个机器启动一个。但是创建的queue
只会放在一个RabbitMQ
实例上面,但是其他的实例都同步了这个queue
的元数据。在消费的时候,如果连接到了另一个实例,他会从拥有queue
的那个实例获取消息然后再返回。
这种方式并没有做到所谓消息的高可用,就是个普通的集群,这样还会导致要么消费者每次随机连接一个实例然后拉取数据,这样的话在实例之间会产生网络传输,增加系统开销,要么固定连接那个queue
所在的实例消费,这样会导致单实例的性能瓶颈。
而且如果那个方queue
的实例宕机了,会导致接下来其他实例都无法拉取数据;如果没有开启消息的持久化会丢失消息;就算开启了消息的持久化,消息不一定会丢,但是也要等这个实例恢复了,才可以继续拉取数据。
所以这个并没有提供高可用,这种方案只是提高了吞吐量,也就是让集群中多个节点来服务某个queue
的读写操作。
镜像集群模式
镜像集群模式创建的queue
,无论元数据还是queue
里面是消息数据都存在多个实例当中,然后每次写消息到queue
的时候,都会自动把消息到多个queue里进行消息同步。
这种模式的好处在于,任何一台机器宕机了,其他的机器还可以使用。 坏处在于:
- 性能消耗太大,所有机器都要进行消息的同步,导致网络压力和消耗很大。
- 没有扩展性可言,如果有一个
queue
负载很重,就算加了机器,新增的机器还是包含了这个queue
的所有数据,并没有办法扩展queue
。
如何开启镜像集群模式:在控制台新增一个镜像集群模式的策略,指定的时候可以要求数据同步到所有节点,也可以要求同步到指定节点,然后在创建queue
的时候,应用这个策略,就会自动将数据同步到其他的节点上面去了。
2.13.3 RabbitMQ集群如何保证高可用
- 多节点集群:
RabbitMQ
高可用性集群通常由多个RabbitMQ
节点组成,这些节点分布在不同的物理或虚拟服务器上。多节点集群允许负载分散和故障恢复。 - 数据复制:
RabbitMQ
集群使用镜像队列(Mirrored Queues
)来复制队列中的消息和元数据。这意味着队列的数据被复制到多个节点,确保即使一个节点发生故障,数据仍然可用。 - 队列镜像:在创建队列时,可以将队列标记为镜像队列,以确保队列的数据在多个节点上进行复制和同步。这提供了数据的冗余和高可用性。
- 交换机镜像:交换机也可以配置为镜像交换机,确保消息路由规则在多个节点上保持一致。
- 自动故障转移:
RabbitMQ
集群支持自动故障转移,当一个节点发生故障时,集群中的其他节点可以自动接管工作,继续处理消息。 - 负载均衡:
RabbitMQ
集群可以使用负载均衡器,将客户端请求均匀分布到多个节点,提高吞吐量和可用性。 - 集群监控:部署监控系统以实时监测
RabbitMQ
集群的性能和状态。这有助于及时识别问题并采取适当的措施。 - 备份和恢复策略:定期备份
RabbitMQ
数据,以防止数据丢失,并建立有效的数据恢复策略,以应对不同类型的故障情况。 - 节点故障检测:使用节点监测工具,定期检测节点的健康状态,以及时发现和处理故障。
- 定期维护和升级:定期进行
RabbitMQ
的维护和升级,以确保系统的稳定性和安全性。
2.14 RabbitMQ的死信队列
RabbitMQ 的死信队列(Dead Letter Queue,DLQ)是一种特殊的队列,用于存储那些无法被消费者正常处理的消息。当一条消息满足一些特定的条件时,它会被标记为死信,并被重新路由到一个死信队列,而不是被丢弃或无限地重新尝试。
- 死信交换机:DLX,
dead-letter-exchange
- 利用DLX,当消息在一个队列中变成死信 (
dead message
) 之后,它能被重新publish
到另一个Exchange
,进而推送到死信队列,这个Exchange
就是DLX。
产生死信队列的情况
- 消息被拒绝
(basic.reject / basic.nack)
:the message was rejected with requeue parameter set to false
(消息被消费者使用basic.reject
或basic.nack
方法,并且requeue
参数设置为false
,通过这种方式进行消息确认)。 - 消息TTL过期:
the message TTL has expired
(消息过期),消息在队列的存活时间超过设置的生存时间(TTL)时间。如果不设置消息过期时间,那么消息默认是不会过期的。 - 消息队列的消息数量已经超过最大队列长度。
将消息转发到死信队列的过程
- 声明死信
Exchange
。 - 声明死信
Queue
,并绑定到死信Exchange
。 - 在正常
Queue
设置dead-letter-exchange
和dead-letter-routing-key
,指向死信Exchange
和routing key
。
当消息成为死信后,RabbitMQ
会根据设置将消息重新发布到死信Exchange
,最终到达死信Queue
。
处理死信的常见方式
- 日志记录死信信息,便于追踪死信原因。
- 发送告警通知开发人员。
- 修正错误后将死信重新发回正常
Queue
。 - 存储死信到数据库等其他存储系统。
死信队列实现了失败消息的异步处理和解耦,十分适合处理不可路由、不可消费的问题消息。
2.15 RabbitMQ有哪些负载均衡策略?
RabbitMQ
支持不同的负载均衡策略,这些策略用于确定消息在多个消费者之间的分发方式。以下是RabbitMQ
中常用的负载均衡策略:
- Round Robin(轮询):这是默认的负载均衡策略。在轮询中,每个消息按顺序发送给每个消费者。当有多个消费者时,每个消费者依次接收消息,以平均分配负载。
- Least Connections(最小连接数):消息将发送到当前具有最少连接数的消费者。这样可以确保消息发送到当前负载最轻的消费者上,从而实现负载均衡。
- Consistent Hash Exchange(一致性哈希交换):这种策略使用一致性哈希算法,将消息路由到相同的消费者,以确保特定的消息一直被相同的消费者处理。这对于需要保持消息处理的顺序性或者需要缓存一些处理结果的情况很有用。
- Random(随机):消息将被随机地分发给一个消费者。这种策略适用于不需要关心消费者的连接数或处理能力,只关心随机分配的情况。
- Breadth-First(广度优先):消息将被发送给队列中当前连接数最少的消费者。这有助于确保消息被分发给队列上连接数较少的消费者,以实现整体上的负载均衡。
- Depth-First(深度优先):消息将被发送给队列中当前连接数最多的消费者。这种策略可能用于确保消息被发送给队列上连接数最多的消费者,以便更快地处理消息。
这些负载均衡策略可以根据特定的应用场景和需求进行选择。选择合适的策略可以帮助优化消息的分发,确保系统的性能和可伸缩性。
2.16 RabbitMQ实现一个延时队列,该怎么去做?
在RabbitMQ中实现延时队列通常可以使用RabbitMQ的插件rabbitmq_delayed_message_exchange来实现。该插件允许你创建一个支持延时消息投递的交换机(Exchange)。下面是一些步骤,帮助你在RabbitMQ中实现延时队列:
- 安装RabbitMQ插件:
首先,确保RabbitMQ服务器上已经安装了rabbitmq_delayed_message_exchange插件。你可以通过以下命令进行安装:rabbitmq-plugins enable rabbitmq_delayed_message_exchange
安装完成后,重启RabbitMQ服务。
2. 定义延时交换机:
创建一个延时交换机,该交换机使用x-delayed-message类型,并设置x-delayed-type为实际交换机的类型,例如direct、fanout、topic等。
rabbitmqadmin declare exchange name=delayed_exchange type=x-delayed-message arguments='{"x-delayed-type":"direct"}'
- 定义实际交换机和队列:
创建实际的交换机和队列,这是消息最终要发送到的地方。
rabbitmqadmin declare exchange name=actual_exchange type=direct
rabbitmqadmin declare queue name=actual_queue
rabbitmqadmin declare binding source=actual_exchange destination=actual_queue routing_key=your_routing_key
- 发送延时消息:
发送消息到延时交换机,并设置消息的x-delay头部来指定延时时间(以毫秒为单位)。
rabbitmqadmin publish exchange=delayed_exchange routing_key=your_routing_key payload=your_message properties='{"delivery_mode":2,"headers":{"x-delay":5000}}'
上述命令中,x-delay头部设置为5000表示延时5000毫秒(即5秒)。
这样,消息将会在经过指定的延时后,被发送到实际的交换机和队列中。请确保替换上述命令中的占位符(your_routing_key、your_message)为实际的值。
注意:rabbitmqadmin是RabbitMQ的命令行工具,你需要安装并配置它才能使用上述命令。另外,RabbitMQ插件的版本和RabbitMQ服务器的版本可能有关联,确保使用兼容的版本。
3 设计一个抢红包系统
参考1:设计一个抢红包系统
参考2:怎么设计一个抢红包系统?