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

ops/2024/11/26 11:25:22/

说在前面

在如今的高并发互联网应用中,如何确保系统在巨大的流量冲击下还能稳定运行,是每个技术团队都会遇到的挑战。说到这,消息队列(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的优势,为系统的稳定性和性能提供有力保障。


http://www.ppmy.cn/ops/136816.html

相关文章

Javaweb关于web.xml的相关配置信息

Javaweb关于web.xml的相关配置信息 初始页面 <!-- 规定加载进入的初始页面--> <welcome-file-list><welcome-file>/login.jsp</welcome-file> </welcome-file-list>配置Servlet <!--配置Servlet--> <servlet><servlet-name&g…

selinux 和 防火墙

1、selinux的说明 NSA为了控制这方面的权限与进程的问题&#xff0c;就使用linux来作为研究目标&#xff0c;最后将研究的成果整合到linux内核里面去&#xff0c;也就是SELinux。 SELinux是Security-Enhanced Linux的缩写&#xff0c;意思是安全强化的linux。 SELinux 主要由美…

[C++]vector:迭代器失效和vector<string>中更深层次拷贝的问题

迭代器失效 一、迭代器失效的两大类型异地扩容后的失效原地删除后的失效 二、vector<string>出现的深层次拷贝问题 一、迭代器失效的两大类型 异地扩容后的失效 在vector容器中&#xff0c;通常情况下的扩容都是异地扩容&#xff0c;例如resize, reserve&#xff0c;但…

文件分片上传

分片上传&#xff1a; 1、前端&#xff08;vue2elementui&#xff09; <template><div><el-upload:http-request"handleUpload":before-upload"beforeUpload"multiple:auto-upload"false":on-change"handleFileChange&quo…

react项目初始化配置步骤

1.npx create-react-app 项目名称 vue项目同理 2.去编辑器市场安装所需插件&#xff0c;例如ESlint以及Prettier-Code formatter formatiing-toggle 3.在项目中安装 ESLint 和 Prettier 及相关插件&#xff1a; 3.1&#xff1a; npm install --save-dev eslint prettier 3.2…

非递归遍历二叉树(数据结构)

我的博客主页 非递归遍历二叉树 前序遍历&#xff08;迭代&#xff09;中序遍历&#xff08;迭代&#xff09;后续遍历&#xff08;迭代&#xff09; 二叉树的遍历方式有&#xff1a;前序遍历、中序遍历、后续遍历&#xff0c;层序遍历&#xff0c;而树的大部分情况下都是通过递…

使用 Python 的 pdfplumber 库高效解析 PDF 文件

使用 Python 的 pdfplumber 库高效解析 PDF 文件 PDF 文件是日常办公和数据处理中常见的文件格式&#xff0c;而 pdfplumber 是一个专为 PDF 文件解析设计的 Python 库&#xff0c;可以轻松提取文本、表格、图像等内容。本文将介绍 pdfplumber 的基本功能、使用方法&#xff0…

计算机网络谢希仁第七章课后题【背诵版本】

目录 【7-01】计算机网络都面临哪几种威胁?主动攻击和被动攻击的区别是什么?对于计算机网络的安全措施都有哪些? 【7-02】 试解释以下名词:(2)拒绝服务;(3)访问控制;(4)流量分析;(5)恶意程序。 【7-03】为什么说&#xff0c;计算机网络的安全不仅仅局限于保密性?试举例说…