MQ核心作用、解耦、削峰使用场景详解

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 【11月更文挑战第21天】在如今的高并发互联网应用中,如何确保系统在巨大的流量冲击下还能稳定运行,是每个技术团队都会遇到的挑战。说到这,消息队列(MQ)就是背后的“大功臣”了。无论是异步处理请求、平滑应对流量高峰,还是让各个系统模块相互独立不“拖后腿”,MQ都是不可或缺的帮手。那么,MQ是如何削峰的?或者它是如何让复杂系统解耦的?今天,我们就来聊聊MQ的三大核心功能,看它是如何助力系统高效、稳定运转的。


说在前面

在如今的高并发互联网应用中,如何确保系统在巨大的流量冲击下还能稳定运行,是每个技术团队都会遇到的挑战。说到这,消息队列(MQ)就是背后的“大功臣”了。无论是异步处理请求、平滑应对流量高峰,还是让各个系统模块相互独立不“拖后腿”,MQ都是不可或缺的帮手。那么,MQ是如何削峰的?或者它是如何让复杂系统解耦的?今天,我们就来聊聊MQ的三大核心功能,看它是如何助力系统高效、稳定运转的。

1. 什么是MQ(消息队列)?

消息队列(Message Queue,简称MQ)其实就是一个“管道”,用来在不同的系统或服务之间传递消息。想象一下,它像是邮局,发信人把信件交给邮局,邮局再按照顺序把信送到收件人手中,整个过程大家各做各的事,发信人不用担心收件人有没有立刻收到信,这样大家的工作互不干扰。

在系统中,MQ主要负责消息的传递和异步处理。它帮助系统之间进行消息传递,同时还能实现系统的解耦和高效的异步处理。常见的MQ工具包括RabbitMQ、Kafka、ActiveMQ等。

2. MQ的历史与背景

2.1 MQ的诞生历程

1983年,一个在MIT工作的印度小伙突发奇想,以前我们的软件相互通信,都是点对点的,而且要实现相同的协议,能不能有一种专门用来通信的中间件,就像主板(BUS)一样,把不同的软件集成起来呢?于是他搞了一家公司(Teknekron),开发了世界上第一个消息队列软件The Information Bus(TIB)。最开始的时候,它被高盛这些公司用在金融交易里面。因为TIB实现了发布订阅(Publish/Subscribe)模型,信息的生产者和消费者可以完全解耦,这个特性引起了电信行业特别是新闻机构的注意。1994年路透社收购了Teknekron。

TIB的成功马上引起了业界大佬IBM的注意,他们研发了自己的IBM MQ(IBMWesphere)。后面微软也加入了这场战斗,研发了MSMQ。这个时候,每个厂商的产品是孤立的,大家都有自己的技术壁垒。比如一个应用订阅了IBM MQ的消息,如果有要订阅MSMQ的消息,因为协议、API不同,又要重复去实现。为什么大家都不愿意去创建标准接口,来实现不同的MQ产品的互通呢?跟现在微信里面不能打开淘宝页面是一个道理(商业竞争)。

J2EE制定了JDBC的规范,那么各个数据库厂商自己去实现协议,提供jar包,在Java里面就可以使用相同的API操作不同的数据库了。MQ产品的问题也是一样的,2001年的时候,SUN公司发布了JMS规范,它想要在各大厂商的MQ上面统一包装一层Java的规范,大家都只需要针对API编程就可以了,不需要关注使用了什么样的消息中间件,只要选择合适的MQ驱动。但是JMS只适用于Java语言,它是跟语言绑定的,没有从根本上解决这个问题(只是一个API)。

所以在2006年的时候,AMQP规范发布了。它是跨语言和跨平台的,真正地促进了消息队列的繁荣发展。2007年的时候,Rabbit技术公司基于AMQP开发了RabbitMQ 1.0。因为Erlang是作者Matthias擅长的开发语言,第二个就是Erlang是为电话交换机编写的语言,天生适合分布式和高并发。为什么要取Rabbit Technologies这个名字呢?因为兔子跑得很快,而且繁殖起来很疯狂。从最开始用在金融行业里面,现在RabbitMQ已经在世界各地的公司中遍地开花。国内的绝大部分大厂都在用RabbitMQ,包括头条、美团、滴滴(TMD)、去哪儿、艺龙、淘宝等

2.2 MQ的发展现状

随着分布式系统的广泛应用,MQ技术得到了极大的发展。目前市场上常见的MQ产品包括RabbitMQ、Kafka、RocketMQ等,它们各自具有不同的特点和优势,适用于不同的业务场景。例如,RabbitMQ以其高并发和稳定性著称,Kafka则以其高吞吐量和实时性闻名,而RocketMQ则更适合大规模分布式系统应用

3. MQ的核心作用

3.1 异步处理

异步是MQ最重要的作用之一。所谓异步,就是说你不用等到一个任务完成再进行下一个操作,而是把任务交给MQ处理,自己可以继续做别的事情。这就好比你把某项任务外包给了一个帮手(MQ),然后自己继续处理其他工作,等MQ把任务完成后,你再去处理结果。

使用异步MQ的好处:

  • 提高系统性能:不用等待任务完成,能立即处理其他任务。
  • 用户体验更好:用户发起请求后,系统快速响应,而后台的复杂操作可以慢慢处理。

举个例子:在电商系统中,用户下单后,系统需要给仓库发通知,让他们准备发货。如果没有MQ,系统可能会等到仓库那边处理完才告诉用户下单成功,这样用户就得等很久。但有了MQ,系统可以先快速告诉用户“订单已成功”,后续的仓库处理则通过MQ异步通知,用户不用等待后台所有流程结束

示例代码(Spring Boot RabbitMQ)

java复制代码
// 生产者: 将消息发送到消息队列
@Component
public class OrderProducer {
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendOrderMessage(String orderId) {
        rabbitTemplate.convertAndSend("orderQueue", orderId); // 异步发送订单消息
    }
}
// 消费者: 从队列中接收消息并处理
@Component
@RabbitListener(queues = "orderQueue")
public class OrderConsumer {
@RabbitHandler
public void handleOrderMessage(String orderId) {
// 模拟订单处理逻辑
        System.out.println("Processing order: " + orderId);
    }
}

在这个例子中,OrderProducer会把订单消息发送到orderQueue队列,OrderConsumer异步处理订单,用户不会感受到后台的复杂逻辑,只会收到下单成功的反馈

3.2 削峰填谷

削峰是MQ的另一个核心作用。削峰的意思就是把系统中突然涌入的高并发请求“削平”,让系统在面对流量激增时不至于崩溃。它就像一个“水库”,把瞬间涌入的洪水存储起来,等流量回归正常后,再慢慢放出处理。

使用削峰MQ的好处:

  • 防止系统过载:面对突发的高并发流量,系统不会因为超出负载而崩溃。
  • 平滑处理流量:高峰时段通过MQ把请求排队,等流量稳定后再逐步处理,保证系统不会因为短时间的流量激增导致性能下降。

举个例子:在秒杀活动中,用户同时发起大量请求,如果系统直接处理这些请求,服务器可能会崩溃。通过MQ,可以先把这些请求排队,等流量稳定后,系统再逐步处理队列中的请求。这样不仅能保障服务器的稳定,还能让用户体验到秒杀服务的顺畅

示例代码

java复制代码
// 秒杀请求发送到消息队列中进行削峰处理
@Component
public class SeckillProducer {
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendSeckillMessage(String seckillId) {
        rabbitTemplate.convertAndSend("seckillQueue", seckillId); // 秒杀请求排队
    }
}
// 消费者从队列中获取秒杀请求,按顺序处理
@Component
@RabbitListener(queues = "seckillQueue")
public class SeckillConsumer {
@RabbitHandler
public void handleSeckillMessage(String seckillId) {
// 模拟处理秒杀请求逻辑
        System.out.println("Processing seckill request: " + seckillId);
    }
}

通过MQ把秒杀请求排队,可以平滑处理突发流量,避免系统短时间内因为并发量太大而崩溃

3.3 系统解耦

解耦是MQ的第三大作用,简单来说就是让系统模块之间互不干扰,减少系统之间的依赖。在没有MQ的情况下,系统A和系统B可能需要直接进行同步通信,但这样耦合度太高,如果某个系统出现问题,另一个系统也会受到影响。

有了MQ之后,系统A不需要等系统B处理完,它只需要把消息发送到MQ,系统B根据自己的情况异步处理消息。这样系统A和系统B之间就实现了解耦,A不用管B是否忙碌,B也不需要马上响应A的请求。

使用解耦MQ的好处:

  • 降低系统之间的依赖:每个系统可以独立处理自己的逻辑,互不影响。
  • 提高系统灵活性:系统之间通过MQ通信,如果某个系统宕机,MQ可以暂存消息,待系统恢复后继续处理。

举个例子:在电商系统中,订单服务和库存服务需要通信。如果没有MQ,订单系统下单后必须等待库存系统确认库存后才能继续处理。但通过MQ,订单系统下单后,可以把消息发到MQ里,库存系统慢慢去处理,不会影响订单服务的流程

示例代码

java复制代码
// 订单系统发送消息到库存系统
@Component
public class InventoryProducer {
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendInventoryMessage(String orderId) {
        rabbitTemplate.convertAndSend("inventoryQueue", orderId); // 订单消息发送到库存服务
    }
}
// 库存系统异步处理订单消息
@Component
@RabbitListener(queues = "inventoryQueue")
public class InventoryConsumer {
@RabbitHandler
public void handleInventoryMessage(String orderId) {
// 模拟库存扣减逻辑
        System.out.println("Processing inventory for order: " + orderId);
    }
}

通过MQ实现解耦后,订单服务可以快速响应用户的下单操作,而库存服务则异步处理库存扣减操作,两个系统之间互不干扰,降低了耦合度

4. MQ的底层原理逻辑

4.1 MQ的基本架构

MQ的基本架构包括生产者(Producer)、消息队列(Queue)、消费者(Consumer)和消息代理(Broker)等组件。生产者负责将消息发送到消息队列,消费者从消息队列中接收并处理消息,消息代理则负责消息的存储和转发

4.2 消息传递机制

MQ的消息传递机制主要有两种:点对点(Point-to-Point)和发布/订阅(Publish/Subscribe)。

  • 点对点(Point-to-Point):生产者将消息发送到特定的队列,消费者从该队列中拉取消息进行处理。这种模式下,每条消息只能被一个消费者消费。
  • 发布/订阅(Publish/Subscribe):生产者将消息发布到一个或多个主题,订阅了该主题的消费者都可以接收到消息。这种模式下,每条消息可以被多个消费者消费

4.3 消息持久化与可靠性

为了保证消息的可靠性,MQ通常支持消息持久化功能。即将消息存储在磁盘上,即使消息代理崩溃,也能在重启后恢复消息。此外,MQ还提供了各种可靠性机制,如重试机制、死信队列等,以确保消息能够被成功消费

5. 使用场景详解

5.1 异步通知

在异步通知的场景下,MQ能够帮助系统及时响应用户的请求,同时后台慢慢处理后续逻辑。

示例场景一:用户注册后发送欢迎邮件

当用户注册成功后,系统通过MQ异步发送邮件,不用阻塞用户的注册流程。这样可以提高用户体验,同时避免因为发送邮件而延迟用户注册成功的时间

示例场景二:订单完成后发送优惠券

用户完成订单后,优惠券通过MQ异步发放,订单流程不会被拖慢。这样可以确保订单流程的顺畅进行,同时给用户带来更好的购物体验

5.2 削峰场景

在高并发场景下,MQ可以有效地进行削峰处理。

示例场景一:电商秒杀活动

在秒杀活动中,大量用户同时请求,MQ通过把请求排队来平滑处理流量,避免服务器崩溃。这样可以确保秒杀活动的顺利进行,同时提高系统的稳定性

示例场景二:支付系统高峰期

当大量用户发起支付请求时,MQ可以帮助系统按顺序处理,避免并发过高导致支付系统瘫痪。这样可以确保支付系统的稳定运行,同时提高用户的支付体验

5.3 系统解耦

在需要解耦的场景下,MQ是一个理想的选择。

示例场景一:电商系统中的订单与库存解耦

订单服务和库存服务通过MQ进行异步通信,避免耦合过高导致的问题。这样可以提高系统的灵活性和可维护性,同时降低系统之间的依赖

示例场景二:日志系统与业务系统解耦

日志系统可以通过MQ收集各个模块的日志信息,业务系统只需把日志发给MQ,不需要直接与日志系统通信。这样可以提高日志系统的可扩展性和可靠性,同时降低业务系统的复杂度

6. Java模拟场景

6.1 异步通知场景模拟

以下是一个使用Java和RabbitMQ模拟异步通知场景的示例代码。

生产者代码

java复制代码
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class AsyncNotificationProducer {
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendNotification(String userId, String message) {
// 发送异步通知消息到消息队列
        rabbitTemplate.convertAndSend("notificationQueue", userId + ":" + message);
    }
}

消费者代码

java复制代码
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class AsyncNotificationConsumer {
@RabbitListener(queues = "notificationQueue")
public void receiveNotification(String message) {
// 处理异步通知消息
        String[] parts = message.split(":");
String userId = parts[0];
String notification = parts[1];
        System.out.println("Received notification for user " + userId + ": " + notification);
// 在这里可以添加发送邮件或短信的逻辑
    }
}

6.2 削峰场景模拟

以下是一个使用Java和RabbitMQ模拟削峰场景的示例代码。

生产者代码

java复制代码
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class PeakShavingProducer {
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendRequest(String requestId) {
// 发送请求到消息队列进行削峰处理
        rabbitTemplate.convertAndSend("peakShavingQueue", requestId);
    }
}

消费者代码

java复制代码
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class PeakShavingConsumer {
@RabbitListener(queues = "peakShavingQueue")
public void handleRequest(String requestId) {
// 处理削峰后的请求
        System.out.println("Processing request: " + requestId);
// 在这里可以添加处理请求的逻辑
    }
}

6.3 系统解耦场景模拟

以下是一个使用Java和RabbitMQ模拟系统解耦场景的示例代码。

生产者代码(订单服务)

java复制代码
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class OrderProducer {
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendOrder(String orderId) {
// 发送订单消息到库存服务
        rabbitTemplate.convertAndSend("inventoryQueue", orderId);
    }
}

消费者代码(库存服务)

java复制代码
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class InventoryConsumer {
@RabbitListener(queues = "inventoryQueue")
public void handleOrder(String orderId) {
// 处理库存扣减逻辑
        System.out.println("Processing inventory for order: " + orderId);
// 在这里可以添加库存扣减的逻辑
    }
}

7. 总结

MQ(消息队列)的核心作用主要体现在异步处理、削峰和解耦。通过异步处理,系统可以提升响应速度,提高用户体验;通过削峰,系统可以在面对高并发流量时稳定运行,避免过载;通过解耦,系统之间可以减少依赖,提升灵活性和可维护性。

无论是在电商系统的订单处理、秒杀场景,还是系统模块的解耦设计中,MQ都是一个强大的工具。通过MQ,系统能够更好地应对复杂的业务场景和高并发需求,保持稳定、高效的运行。

作为资深MQ专家,我们不仅要熟练掌握MQ的核心功能和底层原理,还要能够根据具体的业务场景选择合适的MQ产品和配置方案,以充分发挥MQ的优势,为系统的稳定性和性能提供有力保障。

相关实践学习
消息队列RocketMQ版:基础消息收发功能体验
本实验场景介绍消息队列RocketMQ版的基础消息收发功能,涵盖实例创建、Topic、Group资源创建以及消息收发体验等基础功能模块。
消息队列 MNS 入门课程
1、消息队列MNS简介 本节课介绍消息队列的MNS的基础概念 2、消息队列MNS特性 本节课介绍消息队列的MNS的主要特性 3、MNS的最佳实践及场景应用 本节课介绍消息队列的MNS的最佳实践及场景应用案例 4、手把手系列:消息队列MNS实操讲 本节课介绍消息队列的MNS的实际操作演示 5、动手实验:基于MNS,0基础轻松构建 Web Client 本节课带您一起基于MNS,0基础轻松构建 Web Client
相关文章
|
2月前
|
消息中间件 存储 监控
消息队列系统中的确认机制在分布式系统中如何实现
消息队列系统中的确认机制在分布式系统中如何实现
|
2月前
|
消息中间件 存储 监控
消息队列系统中的确认机制在分布式系统中如何实现?
消息队列系统中的确认机制在分布式系统中如何实现?
|
4月前
|
缓存 NoSQL 关系型数据库
消息系统的4大使用场景
消息系统的4大使用场景
44 0
|
6月前
|
消息中间件 中间件 API
中间件解耦、异步与削峰
【6月更文挑战第17天】
77 5
|
5月前
|
消息中间件 存储 RocketMQ
MetaQ/RocketMQ 原理问题之在解耦场景中,消息队列工作的问题如何解决
MetaQ/RocketMQ 原理问题之在解耦场景中,消息队列工作的问题如何解决
|
5月前
|
消息中间件 存储 缓存
架构设计篇问题之消息队列(MQ)在微服务系统中问题如何解决
架构设计篇问题之消息队列(MQ)在微服务系统中问题如何解决
|
6月前
|
消息中间件 中间件 API
中间件消息队列的优势解耦
【6月更文挑战第7天】
61 3
|
6月前
|
消息中间件 中间件
中间件消息队列的优势流量削峰
【6月更文挑战第7天】
90 3
|
6月前
|
消息中间件 中间件
中间件消息队列的优势异步处理
【6月更文挑战第7天】
54 3
|
7月前
|
消息中间件 缓存 API
【后端面经】【消息队列】22 | 消息队列:消息队列可以用来解决什么问题?-03 扩展性+可用性+事件驱动思想
【5月更文挑战第8天】 本文探讨了扩展性、可用性和事件驱动的概念。扩展性方面,消息队列简化了新下游的接入,而同步调用需要复杂的协调。在保证高可扩展性和研发效率的设计中,若无法使用消息队列,可以提供一致性抽象来减轻接入负担。可用性上,消息队列只需确保消息发送,而同步调用需保证所有下游成功,更易出错。事件驱动是一种通过事件进行组件间通信的架构模式,具有低耦合、高扩展性和高可用性,适合处理复杂流程。结合SAGA的事件驱动方案能实现高级分布式事务管理,即使实时性稍弱,但能保证事务的异步和高效执行。
58 1