一 MQ的概述
消息中间件,其实准确的叫法应该叫消息队列(message queue),简称MQ。
MQ一般用在几种场景:
流量削锋:流量过大的时候,用MQ作为一个中间层,暂时存储流量,让流量在队列中排队去访问服务,从而控制直接访问服务的流量,减轻服务的实时流量压力。
应用解耦:使用MQ作为应用之间的中间层,从而使得应用直接不存在直接调用的关系,解除应用之间的耦合。这样在被调用的应用挂掉以后,应用之间的调用不会直接产生异常,请求仍可以正常发送,待被调用应用重新起来以后,再去消费处理MQ中挤压的调用请求,为系统的修复争取到了时间。
异步任务:有些服务调用对于响应的实时性要求不高,允许延迟响应、异步处理。使用MQ可以将这些可以异步处理的请求,暂存在队列中,调用方不必等待,直接向下执行其他任务,被调用方消费MQ中消息后自行返回给调用方响应。
三个角度去关注MQ即可抓住MQ的核心:
- 消息可靠性:消息可靠性,即消息会不会丢失?围绕防止消息丢失做了哪些工作?
2.消息模型:消息模型,即支持以什么样的模式去消费消息?点对点?广播?发布订阅?其消息模型丰富度如何?
- 吞吐量:MQ作为用来减轻系统压力的中间件,其自身势必会经常面对很大的流量,吞吐量如何自然是要考虑的
RabbitMQ有几个特点:
1遵从AMQP协议
2丰富的消息模型极
3消息可靠性高但是吞吐量不高
1.遵从AMQP
AMQP简单来说就是规定好了MQ的各个抽象组件,RabbitMQ则是一款完全严格按照AMQP来实现的开源MQ,使得很好被开源框架所集成,比如Spring AMQP专门就是用来操作AMQP架构的中间件的,因此RabbitMQ可以被Spring Boot很方便的集成。
2.丰富的消息模型
简单队列:简单队列,consumer和producer通过队列直连。
工作队列:工作队列(work queue),让多个消费者去消费同一个消息队列中的消息,支持轮询分发(默认)、公平分发两种分发模式。
订阅队列:订阅模式(fanout),也叫广播模式,见名知意,其特点是将消息广播出去。通过交换机将生产者生产的消息分发到多个队列中去,从而支持生产者生产的一个消息被多个消费者消费。
路由模式:路由模式(direct),在订阅模式支持一条消息被多个消费者消费的特性上增加了分类投递的特性,通过交换机,支持消息以类别(routing key)的方式投送到不同的消息队列中去。
主题模式:主题模式,也叫通配符模式,在路由模式以类别进行消息投送的基础上增加了对通配符的支持,这样就可以使用通配符将多个类别聚合成一个主题。
RPC:不会
3.消息可靠性高但是吞吐量不高
RabbitMQRabbitMQ 提供了多种机制来确保消息的可靠性,包括持久化、消息确认、发布确认等。这些机制确保消息不会丢失,并且能够在各种情况下处理消息传递失败。但是由于存在这些用于保证消息可靠性的机制,RabbitMQ的吞吐量在三大中间件中是最低的。
在消息中有三种可能性造成数据丢失:
1消费者消费消息失败
2生产者生产消息失败
3MQ数据丢失
消费者消费失败的原因
RabbitMq存在应答机制,默认为自动应答,MQ向消费者推送一条消息,消费者收到这条消息后会返回一个ack(应答)给MQ,MQ收到应答后会删除这条消息
自动应答是啥意思
在RabbitMQ中,消费者会从消息队列中取出消息。当消费者收到一条消息时,如果启用了自动应答(即默认设置),它会立即向RabbitMQ发送一个确认(ack),表示它已经收到了这条消息。RabbitMQ在收到ack的确认后,就会把这条消息从队列中删除,认为任务完成了。
解释为什么出现这个问题
现在,我们来看看这个自动应答会带来什么问题。假设消费者在接收到消息后,立刻发送了ack确认,但在这之后,消费者发生了故障(比如断电、崩溃等),还没来得及处理这条消息。这时候,消息已经被RabbitMQ删除了,因为它认为消费者已经完成了处理。
**这样一来,结果就是这条消息丢失了。**消耗者并没有处理这条消息,RabbitMQ却已经把它删除了,造成数据的丢失。
解决以上列举的数据丢失问题的办法有三种:
1手动应答:为了解决这个问题,RabbitMQ允许我们选择手动应答,也就是说,消费者在处理完消息之后才发送ack确认。这样即使消费者在处理过程中出现问题,RabbitMQ也能够重新将未被确认的消息发送给其他消费者进行处理,确保消息不会丢失。
2消息确认机制:当消息队列收到消息后,告知生产者,让生产者感知到自己生产的消息,消息队列已经接收到,用来解决“生产者生产消息失败”问题。消息确认机制有两种实现方式:
2.1AMQP事务
2.2confirm
3持久化:消息队列的消息持久化到磁盘上,用来解决“MQ数据丢失”问题。
二 Kafka
特点:Kafka,一款具有高吞吐量、高可靠性的分布式消息中间件。其采用分布式架构、顺序写、序列化、零拷贝等机制保证了高吞吐量,数据自动落磁盘完成持久化来保证消息不会丢失。
Kafka和mq的区别
1. 架构与设计理念
Kafka:
Kafka是一个分布式流媒体平台,设计用于处理大规模的数据流。
它采用了分布式架构,支持水平扩展。
数组式的数据存储,支持持久化,并且具有高吞吐量。
MQ(如RabbitMQ、ActiveMQ等):
传统的消息队列系统通常是中心化的,主要用于消息的异步传递与处理。
这些系统一般强调消息的可靠性和顺序传输。
通常不以持久化为主要目标,尤其是RabbitMQ等。
2. 消息存储与传递
Kafka:
消息以日志的形式存储,消费者可以在任意时间读取消息,不会因为已被消费而消失。
支持消息的保留,可以根据时间或大小限制保留消息。
MQ:
消息在被消费者消费后会从队列中移除。
一旦消息被消费,通常会丢失,除非使用了持久化存储。
3. 消费者模型
Kafka:
采用了“发布-订阅”模式,多个消费者可以订阅同一个主题,且可以独立消费消息。
使用消费组机制,可以实现负载均衡。
MQ:
传统MQ通常使用点对点模型(如队列),消息被发送到特定队列,消费者从队列中按顺序获取消息。
消费者组的概念也存在,但一般的实现更简单。
4. 性能和吞吐量
Kafka:
设计时考虑了高吞吐量,能够处理每秒数百万条消息。
支持批量处理,能显著提升性能。
MQ:
性能通常较低,适合相对较小的消息量,设计上更关注于可靠性而非吞吐量。
一般情况下吞吐量和延迟可能会受限于消息确认机制和发送策略。
5. 顺序与分区
Kafka:
提供分区机制,消息在一个主题中的分区可以保证顺序,但不同分区之间则不保证顺序。
通过分区的设计,可以实现高并发消费。
MQ:
通常消息在队列中是有序的,由于消费的点对点模型,通常对顺序要求较高。
6. 使用场景与应用
Kafka:
适合处理大数据流、实时分析、日志聚合、事件源等场景。
主要用于大规模数据管道和流处理。
MQ:
适合任务调度、事件驱动的架构、小型微服务间的消息传递等场合。
更注重低延迟和可靠性的场合。
三 RabbitMQ重复消费
MQ向消费者推送message,消费者向MQ返回ack,告知所推送的消息消费成功。但是由于网络波动等原因,可能造成消费者向MQ返回的ack丢失。MQ长时间(一分钟)收不到ack,于是会向消费者再次推送该条message,这样就造成了重复消费。
如何解决重复消费
用从存储redis记录一下已经消费的message的id,当message被消费前先去存储中查一下消费记录,没有该条message的id则正常消费返回ack,有该条message的id的话不用消费直接返回ack给MQ。
以下是一个使用redis解决重复消费的示例步骤:
1监听器接收MQ队列中的数据。
2利用redis的setnx命令,以消息唯一id为key,以消息内容为value,超时时间设置为10分钟,存入redis中。
3如果能够成功存入,说明没有重复消费,则处理业务,处理完业务后返回ack或者nack确认。
4如果存不进去,则说明重复消费,直接返回ack确认的回调信息就可以了。
什么是幂等性
幂等性是一个计算机科学中的概念,广泛应用于API设计、数据库操作、系统接口等场景。简单来说,幂等性指的是一次操作与多次执行该操作的效果是相同的。换句话说,如果一个操作被执行多次,其最终结果与仅执行一次的结果是相同的,这样的操作就被称为是幂等的。
举例说明
HTTP请求:
GET请求:获取资源的请求通常是幂等的。无论请求一次还是多次,每次返回的结果都是相同的。
PUT请求:通常用于更新资源。如果你发送一个请求,更新某个用户的电子邮件地址为 "example@mail.com",即使你多次发送这个请求,结果都是更新为同样的邮件地址。
DELETE请求:删除操作是幂等的。如果你删除某个资源,无论你是发送一次请求还是发送很多次请求,最终该资源都不会存在。
数据库操作:
增量操作通常不是幂等的,比如 INSERT 操作。多次插入同一条数据可能导致数据重复。
设置操作(例如将某个字段更新为特定值)通常是幂等的。在数据库中,将某个字段更新为一个固定的值,不论更新多少次,结果都是这个固定的值。
为什么幂等性重要?
容错性:在分布式系统中,网络请求可能会失败或者超时,客户端可能会重试同一个请求。如果这些请求是幂等的,那么即使重试也不会引起数据的不一致性或重复操作。
简化应用逻辑:引入幂等性后,应用在处理请求时不需要关注请求被执行几次,简化了错误处理和状态维护的复杂性。
提高系统稳定性:确保操作的幂等性可以避免因重复执行而导致的状态混乱,从而增强系统的可靠性和稳定性。
实现幂等性的方法
唯一标识:在操作中使用一个唯一标识符(如UUID),管理请求的状态。第一个请求会处理,而后重复的请求会被忽略(或返回已存在的状态)。
状态记录:在后端记录操作的状态,确保同样的请求不会对资源产生多次影响。
设计原则:在初期设计API和系统时,考虑到幂等性,明确哪些操作需要是幂等的,如何实现。
总结起来,幂等性是设计健壮、可容错系统的一个基本属性,对于提升系统的可靠性、容错性和一致性极为重要。希望这些解读能帮助你更好地理解幂等性!如果还有疑问,欢迎随时提问。
Mq的消息堆积如何解决:
消息堆积通常是指由于消费者处理速度慢、生产者产生速度快,或者系统出现异常等因素导致消息在队列中积压。长期的消息堆积可能会影响系统性能,甚至导致系统崩溃
1. 增加消费者实例
横向扩展: 可以通过增加消费者的数量来提升处理能力。更多的消费者同时处理消息可以加快消息消费的速度。
负载均衡: 确保消费者是均衡地分布在多个实例上,这样可以充分利用资源。
2. 优化消息处理逻辑
提高处理效率: 检查消费者的代码,找出瓶颈并进行优化。比如,使用更高效的算法或数据结构。
批处理: 如果能一次性处理多条消息,考虑使用批处理的方法来减少处理时间。
异步处理: 通过异步处理的方式提升消费者的响应能力,将消息处理过程分拆成多个步骤,提升并发度。
3. 增加资源
提高硬件配置: 可以通过增加服务器的CPU、内存等资源来提升整体处理能力。
调整队列配置: 在一些MQ系统中,可以调整队列的缓冲区大小或其他相关配置,以支持更高的消息吞吐量。
4. 监控与报警
实施监控: 监控消息队列的长度和处理速度。在消息堆积达到一定阈值时,触发报警。
使用可视化工具: 使用监控工具(如Prometheus、Grafana等)来可视化消息队列的状态,便于实时发现问题。
5. 限制生产者的速度
流控机制: 实施流控策略,如限流,避免生产者过快地发送消息,从而导致队列消息堆积。
背压策略: 一些消息队列系统支持背压机制,当消费者处理消息慢时,系统可以自动降低生产者的发送速率。
6. 使用死信队列
处理异常消息: 如果某些消息由于错误导致无法处理,可以将这些消息转移到死信队列中,避免其影响正常消息的处理。
重试机制: 对处理失败的消息可以设置重试机制,允许其在适当时间后重新投入处理。
7. 优化消息规划
合理的消息设计: 根据实际业务需求合理设计消息体的大小和结构,减少消息粒度,降低处理复杂性。
制定清晰的业务规则: 明确消息的生产和消费流程,合理规划消息的生产数量及频率。
8. 定期清理与归档
定期清理: 针对不必要的过期消息,进行定期清除。
消息归档: 对于不再需要立即处理的老旧消息,可以考虑归档到其他存储,以减轻队列压力。
9. 选择合适的消息队列解决方案
评估现有队列产品: 如果现有的消息队列系统无法满足需求,考虑更换或引入更高级别的MQ产品,比如使用支持分布式、持久化和高可用的消息队列服务。