☰
前端点两次算两条消息吗?消息队列MQ与幂等性入门指南
2026/9/29 1:23:46 网站建设 项目流程

最近在整理技术文章时,看到一条搜索词让我感触特别深:"前端点两次算是发两条消息吗"。这个问题在开发社区里出现的频率,比大家想象中高得多,而且它恰好是理解消息队列MQ的最佳入口。先说结论:如果前端没有做防重复处理,点两次确实会发送两个请求,后端要是也没有幂等控制,就会产生两条订单、扣两次款,反映到MQ上就是往队列里发了两次消息。

这篇文章是一份面向零基础的MQ入门指南,内容上我会把"消息队列到底是什么""核心概念怎么理解""主流产品怎么选""订单、秒杀这些场景怎么落地""重复消费和幂等性怎么解决"一次讲透。不管你是有一定经验的后端开发,还是转行入门的新人,只要你搜索过消息队列、MQ、mq幂等问题、mq重复消费问题这些关键词,这篇文章都值得花半小时认真读完。我会尽量用大白话和真实项目的例子来讲,而不是堆概念。

1. 先说结论:前端点两次到底算不算发了两条消息

1.1 一次真实的下单事故

我认识的一个朋友做电商后端,某天晚上他们接到客服反馈:一个用户在下单时手速快了点,连点两下"提交订单",结果收到了两条扣款短信,后台也真的生成了两笔订单。排查的时候,前端说"我们提交后按钮其实已经禁用了一秒",后端说"我们接口没有加任何防重措施",两边各有各的道理,最后只能给用户退款了事。

这个事故里,前端按钮禁用没生效(可能是网络慢导致按钮状态没来得及切换),后端接口没有做幂等校验(同一个订单请求来了两次,创建了两条记录),问题就这样产生了。如果这个系统的下单链路已经接入了MQ,情况会变成:两个HTTP请求进来,后端业务代码往MQ里投递了两条"创建订单"消息,消费者拉取后还是创建了两条订单。也就是说,消息队列不会自动帮你拦截第二次点击,它只负责把消息可靠地传过去。

1.2 MQ不会消灭重复,它只是把重复问题搬到了消息层

这里有个很多初学者没想透的点:主流MQ产品为了保证消息不丢失,默认采用"至少一次"(at least once)的投递语义。什么叫至少一次?就是同一条消息在极端情况下可能被重复投递。比如消费者处理完业务逻辑后,还没来得及向Broker发送确认(ACK),进程就宕机了,Broker会认为消息没被成功消费,等消费者恢复后重新投递一次。这样消息就实实在在被处理了两遍。

所以请记住这个反直觉的结论:引入MQ之后,"重复"问题不是消失了,而是从"接口层重复调用"转移到了"消息层重复投递"。你在接口层做的防重,和消息层需要的幂等处理,是两个层面的东西。这也是为什么"mq幂等问题"会成为搜索热词——因为它确实是所有MQ使用者在生产环境中绕不开的第一道坎。后面我会用一整章专门讲幂等性怎么做,先把概念地基打好。

2. MQ核心概念拆解:一条消息从生产到消费的完整旅程

2.1 消息队列到底是个什么东西

消息队列(Message Queue,简称MQ)说白了就是一个"中间存储层"。生产者和消费者不再直接互相调用,而是通过这个中间层进行通信。你可以把它想象成快递柜:寄件人把包裹放进柜子里,不用站在原地等收件人;收件人什么时候有空,什么时候来取,柜子会一直帮他存着。

传统同步调用是"打电话":你拨通对方,双方都在线上,必须等对方说完了才能挂。MQ是"发微信":你发一条消息过去,对方看到就回复,没看到消息也躺在服务器里,不会丢。这种异步通信的方式,解决了同步调用中最让人头疼的两个问题:下游接口慢会拖垮上游,下游服务挂了会导致上游调用失败。

从技术实现上看,MQ的核心能力是:存储消息、路由消息、投递消息,以及管理"谁消费了什么"的消费进度。所有你听到的术语,比如队列、主题、分区、偏移量,本质都是围绕这四件事展开的。

2.2 核心角色:生产者、Broker、消费者

一个标准的MQ系统里有三个核心角色:

角色英文职责通俗理解
生产者Producer产生并发送消息的一方寄件人
BrokerBroker消息队列服务本身,负责存储、路由、投递快递中转站
消费者Consumer从Broker拉取消息并处理的一方收件人

在RabbitMQ里,还有个重要的概念叫Exchange(交换机)。生产者不直接把消息塞进队列,而是把消息交给交换机,交换机根据路由规则把消息投递到不同的队列。这就好比快递中转站里有一套分拣规则,根据收件地址把包裹分到对应的片区。而在Kafka里,核心模型是Topic(主题)+ Partition(分区),一个主题的消息会被拆成多个分区存储,消费者组里的每个消费者负责消费一部分分区,从而实现并行消费。

入门阶段不需要把这些差异全部吃透,你只需要建立一个大框架:消息先到Broker,Broker按规则存储,消费者按自己的消费进度去取。后面看任何一款MQ的文档,你都能快速对应上这个框架。

2.3 三种投递语义:为什么"至少一次"是默认选择

消息投递语义是理解MQ行为的关键,一共三种:

  • 至多一次(at most once):消息可能丢,但绝不会重复。性能好,但无法保证可靠。
  • 至少一次(at least once):消息不会丢,但可能重复。这是绝大多数MQ的默认选择。
  • 恰好一次(exactly once):消息既不丢也不重复。听起来最完美,但实现代价极高,通常需要外部存储配合去重。

为什么主流MQ都默认"至少一次"?因为分布式系统里,"不丢消息"比"不重复消息"更容易实现,而且"重复"可以通过消费端的幂等性来兜底,"丢失"却几乎无法挽回。你得理解这个设计取舍:MQ把"不重复"的责任交给了使用方,这是合理的——它根本不知道你的业务里什么算重复,只有业务方自己知道。

3. 主流MQ选型不纠结:RabbitMQ、Kafka、RocketMQ、Pulsar怎么挑

3.1 四款主流MQ的对比表

市面上的MQ产品很多,入门阶段你只需要搞清楚四款主流的开源产品:RabbitMQ、Kafka、RocketMQ、Pulsar。另外有同学会搜到"ibm mq",这是IBM的老牌商业产品,银行、航空这类行业用得多,企业级稳定性没得说,但License费用高、运维成本重,个人学习不推荐。还有"zero mq"(ZeroMQ),它严格来说不算消息队列中间件,而是一个高性能消息库,没有Broker,适合做进程内的点对点通信,别和真正的MQ混在一起。

下面这张表是我根据实际使用体验整理的,尽量客观:

维度RabbitMQKafkaRocketMQPulsar
开发语言ErlangScala/JavaJavaJava
吞吐量中等(万级/秒)极高(百万级/秒)高(十万级/秒)高(十万级/秒)
消息可靠性高高非常高非常高
功能丰富度路由灵活、插件多相对简单,侧重流式事务消息、延迟消息功能全面、多租户
运维复杂度低中中较高
典型场景企业内部业务系统日志采集、大数据管道电商订单、金融交易云原生、多租户平台

3.2 我建议的选型思路

很多新手一上来就问"哪个MQ最好",这其实是错误的问题。选型不看热度,看场景:

如果你做的是企业内部业务系统,需要处理订单通知、短信发送、任务异步化这类请求,RabbitMQ是最稳妥的选择。它路由灵活、部署简单、资料多,社区踩坑经验一抓一大把,小团队完全够用。我就是从这个产品入门的,强烈推荐新手从它开始。

如果你的核心场景是大数据日志采集、用户行为追踪、数据管道,Kafka是当之无愧的标准答案。它的设计初衷就是处理海量日志流,吞吐量吊打其他产品。但要注意,Kafka的消费模型和RabbitMQ不太一样,入门门槛稍微高一点。

如果你们公司是Java技术栈,业务上需要事务消息、延迟消息、消息轨迹这些高级特性,RocketMQ值得考虑。它是阿里巴巴开源的产品,在国内电商场景里经过了大量验证,对中文文档和社区的支持也特别好。

Pulsar更像是"为云原生而生"的新一代产品,多租户、存算分离是它的亮点,但在国内生产环境的使用率还不算高。除非你有明确的多租户需求,否则入门阶段不用优先考虑它。

4. 从订单场景看MQ的三大核心价值:异步、削峰、解耦

4.1 异步化:把20秒的响应变成200毫秒

先看一个典型场景。用户在电商网站下单,订单服务需要做的事包括:写入订单表、扣减库存、增加用户积分、发送短信通知、调用物流系统生成配送单。如果全部同步执行,注意看这个时间线:库存扣减200ms、积分增加300ms、短信服务500ms、物流系统800ms,加上订单写入和网络开销,接口总耗时轻松超过2秒。用户在手机上盯着转圈,体验非常糟糕。

引入MQ后逻辑完全变了:订单服务只做两件事——写入订单表、把一条"订单已创建"的消息投递到MQ,然后立刻返回。整个接口耗时压缩到200ms以内。库存服务、积分服务、短信服务、物流系统各自监听对应的队列,谁有空谁处理,互不阻塞。这就是异步化的威力:把"必须等所有事情做完才返回"变成"核心流程做完就返回,非核心流程交给MQ慢慢处理"。

这里要注意一个设计细节:不是所有步骤都适合异步。订单入库是核心流程,必须同步完成,否则用户都不知道订单到底建没建。而短信通知、积分增加这些"结果就算晚几秒也影响不大"的事情,才适合丢进MQ。异步的目的是提升用户体验,不是把核心业务逻辑变得不确定。

4.2 削峰填谷:秒杀场景为什么必须用MQ

每年大促的时候,系统最怕的就是瞬时流量。假设一个秒杀活动在0点开放,瞬间涌入10万个请求,后端接口每秒只能处理5000个,如果所有请求都直接打到数据库,数据库必然被压垮。

这时候MQ就像一个缓冲区,或者说一个"泄洪闸"。所有秒杀请求先写入MQ,后端服务按照自己能承受的速度(比如每秒5000个)从队列里取请求来处理。流量高峰被削掉了,请求也不会丢,只是排着队慢慢处理。用户看到的可能是"排队中,请稍候",但系统的可用性保住了。这就是削峰填谷:高流量先存起来,低峰期慢慢消费。

不过要提醒一点,削峰填谷不是万能的。如果请求量已经超过了你的消费者处理能力的极限,堆积会越来越严重,最终还是要靠限流、扩容等手段配合。MQ负责把"瞬间峰值"摊平,但不能解决"持续高于处理能力"的根本矛盾。

4.3 服务解耦:订单系统不再依赖十个下游

没有MQ的年代,订单服务创建订单后,需要依次调用库存、积分、短信、物流、优惠券等十来个下游系统。每接入一个新的下游,订单服务就要改一次代码、多一次远程调用。更痛苦的是,如果某个下游接口不稳定,订单主流程都可能被拖垮。

引入MQ之后,订单服务只需要发布一条"订单已创建"事件消息,它根本不需要知道有哪些系统会订阅这个消息。今天新增一个"发票服务",只需要让发票服务自己去订阅消息队列,订单服务的代码一行都不用改。这种"生产者不关心消费者是谁"的机制,就是解耦。

这里也顺便回应一下热搜词"订单mq是什么意思"。在电商开发语境里,所谓"订单MQ"并不是某个特定产品,而是指围绕订单生命周期产生的一系列消息事件,比如order.created(订单创建)、order.paid(订单支付)、order.cancelled(订单取消)等等。订单系统只负责发布事件,下游系统按需订阅,这就是社区里常说的高事件驱动架构,也是MQ最核心的应用方式之一。

5. 手把手跑通首个MQ Demo:Docker安装RabbitMQ并完成收发

5.1 用Docker三步启动RabbitMQ

学习MQ最快的路径就是亲手跑一遍收发消息全流程。我建议用Docker,不用在本机装一堆依赖。执行下面两条命令:

docker pull rabbitmq:3-management docker run -d --name rabbitmq \ -p 5672:5672 \ -p 15672:15672 \ rabbitmq:3-management

5672端口是RabbitMQ的消息通信端口,15672是Web管理界面端口。启动完成后,浏览器打开http://localhost:15672,用默认账号guest、密码guest登录,你就能看到队列、连接、消息速率等信息。这个管理界面对于理解MQ的运行机制非常有帮助,推荐初学者经常点开看看。

如果Docker命令执行报错,大概率是端口被占用,换个映射端口就行。还有个小经验:rabbitmq:3-management这个镜像已经带了管理插件,不需要自己再去启用,省了很多事。

5.2 写一个生产者发送消息

先安装Python操作RabbitMQ的客户端库pika:

pip install pika

然后新建一个send.py文件:

import pika # 1. 建立到RabbitMQ的连接 connection = pika.BlockingConnection( pika.ConnectionParameters('localhost') ) channel = connection.channel() # 2. 声明一个队列,如果队列不存在则创建 # queue_declare 是幂等操作,重复声明不会报错 channel.queue_declare(queue='hello') # 3. 发布一条消息 # exchange 为空字符串表示使用默认交换机,直接投递到指定队列 channel.basic_publish( exchange='', routing_key='hello', body='Hello MQ!' ) print("消息已发送") # 4. 关闭连接 connection.close()

这段代码的流程是:连接RabbitMQ → 创建(或确认存在)一个名为hello的队列 → 往队列里发一条消息 → 关闭连接。注意routing_key='hello'必须和queue_declare的队列名一致,消息才能正确投递到目标队列。

5.3 写一个消费者接收消息

再新建一个receive.py文件:

import pika connection = pika.BlockingConnection( pika.ConnectionParameters('localhost') ) channel = connection.channel() # 同样先声明队列,消费者和生产者都需要确保队列存在 channel.queue_declare(queue='hello') # 定义一个消息处理回调函数 def callback(ch, method, properties, body): print(f"收到消息: {body.decode()}") # 手动发送确认,告诉Broker这条消息处理成功 # 如果不确认,消息会被重新投递 ch.basic_ack(delivery_tag=method.delivery_tag) # 设置预取值:每次最多向消费者推送1条消息 # 避免消费者处理太慢时,积压太多消息在本地 channel.basic_qos(prefetch_count=1) # 注册消费者,绑定回调函数 channel.basic_consume(queue='hello', on_message_callback=callback) print("开始消费,按 Ctrl+C 退出") # 进入阻塞等待状态,持续接收消息 channel.start_consuming()

这里最关键的是basic_ack手动确认。它表示"这条消息我处理完了,你可以删了"。如果把这一行注释掉,你会发现一个现象:消费者每次重启,之前处理过的消息还会再次收到。这就是"至少一次"投递语义的直观体现,也是重复消费问题的根源。

5.4 运行效果与几个值得注意的细节

打开两个终端,先跑python receive.py,再跑python send.py。这时消费者终端会打印出收到消息: Hello MQ!,一个最小的消息收发闭环就跑通了。

我建议你在熟悉基础流程后,再做三个小实验加深理解:

  • 实验一:不启动消费者,连续运行三次send.py,然后打开RabbitMQ管理界面,找到hello队列,你会看到消息数变成了3。这说明消息是存储在Broker里的,不因为暂时没有消费者而丢失。
  • 实验二:给消息带上业务属性。在basic_publish时传properties=pika.BasicProperties(delivery_mode=2),把消息标记为持久化。否则RabbitMQ重启后,队列里的消息会全部丢失。
  • 实验三:同时启动两个消费者进程,然后发送多条消息。你会看到两条消费者基本按轮询方式分配消息,这其实是因为我们在两个进程里声明了同一个队列名,RabbitMQ默认会把消息轮流分发给同一个队列下的多个消费者,这叫"工作队列"模式。

6. 入门必踩的三个经典坑:重复消费、消息丢失、顺序错乱

6.1 重复消费:最普遍也最危险的问题

我见过太多新手,第一次把MQ接入生产环境,就在重复消费上栽了跟头。场景通常是这样的:消费者从队列里拿到一条"增加积分"的消息,执行了数据库更新操作,积分+100。正要给Broker发送ACK确认时,代码抛了个异常,或者进程被重启了。Broker没有收到ACK,判定这条消息处理失败,于是过几秒重新投递。恢复后的消费者再次收到同一条消息,又执行了一次积分+100。用户一分钱没多花,积分却白赚了200。

这个问题的根源在"至少一次"语义:消息被投递的次数不受业务逻辑控制。缓解的手段有两个层次:一是消费端在代码层面做幂等(下一章详细讲),二是配合basic_ack的正确使用——千万不要在业务处理之前就提前确认消息,一定要处理完再确认。你能控制自己的代码,但控制不了网络和进程的意外,所以幂等是唯一可靠的解法。

6.2 消息丢失:生产、存储、消费三层都要防

重复问题之外,消息丢失更让人头疼,因为它连"发现"的机会都不给你。消息在整个生命周期里可能丢在三个环节:

环节丢失原因防护手段
生产者发送时网络抖动,消息没发到Broker开启Publisher Confirm机制,确认消息被Broker持久化后才算发送成功
Broker存储时消息在内存里,RabbitMQ重启后丢失队列和消息都设置持久化(durable=True + delivery_mode=2)
消费者处理时消费者收到消息但没处理完进程挂了手动ACK,处理完成后再确认

你需要把这三个环节的防护全部做上,才算建立起基本的可靠性保障。任何一个环节漏掉,都可能在关键时刻出问题。我见过一个生产事故:团队只给队列设置了持久化,但发消息时没有设置delivery_mode=2,结果RabbitMQ一重启,所有"等待处理"的消息全部清空,用户支付成功但订单状态一直没更新,最后靠人工手动补单才解决。这个教训让我养成了一个习惯:凡是涉及MQ的代码,可靠性参数必须逐项核对。

6.3 顺序错乱:订单MQ场景最怕的事

"订单mq是什么意思"这个热搜词背后,其实藏着一个很实际的需求:订单类消息特别讲究顺序。比如一个订单有"创建订单"→"支付成功"→"取消订单"三条消息,如果消费者先处理了"取消订单"再处理"支付成功",那业务就乱套了——订单已经取消了,怎么还能支付成功?

要保证消息顺序,有几个常用方案:

  • 方案一:一个队列只绑定一个消费者。这是最简单的方案,队列本身就是先进先出的,单个消费者处理自然有序。代价是消费能力有限,不适合高并发场景。
  • 方案二:按业务ID做哈希路由,让同一个订单的消息永远进入同一个队列。比如在Kafka里用订单ID作为Key,那么同一个订单的消息会进入同一个分区,而Kafka的单个分区内是有序的,每个分区只被消费者的一个线程消费,顺序就能保证。
  • 方案三:给消息附加业务序号,消费者在消费前先校验序号,如果发现序号小于等于已经处理的最大序号,说明是乱序消息,直接丢弃或进入重试队列。

实际项目中,方案二用得最多。它既保证了顺序,又没有牺牲太多并发能力。核心就一句话:如果你想保证一堆消息的顺序,就让它们走同一条有序通道。

7. 幂等性设计:让重复消息不产生重复业务

7.1 什么是幂等,为什么MQ场景必须有它

幂等(Idempotent)这个词看起来高深,其实意思很简单:同一个操作,执行一次和执行两次三次,最终结果完全一样。查订单接口是天然幂等的——你查十次,返回的数据都一样。但"给用户积分+100"不是幂等的——执行一次和两次,结果明显不同。

在MQ场景里,我们必须假设"任何一条消息都有可能被重复消费"。这不是悲观主义,而是分布式系统的现实。所以消费者的处理逻辑必须做到:同一个业务消息无论来多少次,只产生一次真正的业务影响。这就是为什么"mq幂等问题"会成为所有MQ学习者的必修课。

我做一个生活化的类比:你在ATM上转账,银行系统的正确做法是"扣款一次,到账一笔"。如果因为网络原因你点了两次提交,银行绝不能扣两次款。银行靠的是交易流水号去重——每一笔转账都有唯一流水号,第二次提交带着同样的流水号,系统查重后直接忽略。MQ的幂等设计,本质就是把这个"唯一凭证"的机制搬到你的业务里。

7.2 三种落地姿势:唯一约束、Redis锁、状态机

  • 姿势一:数据库唯一约束。这是最简单粗暴、也最可靠的方式。你的业务表里加一个"业务唯一键"字段,比如order_id,并加上唯一索引。重复消费时,第二次插入会因为唯一键冲突而失败,数据库层面直接拦住。
  • 姿势二:Redis SETNX。利用Redis的SETNX命令(只在key不存在时才能设置成功)实现一个"是否已处理"的标记。第一次消费时设置成功,正常处理;重复消费时发现key已存在,直接跳过。要注意设置过期时间,防止标记永久占用内存。
  • 姿势三:状态机校验。适合有明确状态流转的业务,比如订单从"待支付"到"已支付"。消费者在更新前先查一次当前状态,如果已经是"已支付",就不再执行更新。这种方式不需要额外存储,但要求你设计好状态流转规则。

7.3 幂等设计的代码示例

基于Redis的幂等判断是最常见的做法,我贴一段核心逻辑:

import redis r = redis.Redis(host='localhost', port=6379, db=0) def handle_order_message(order_id: str): # 用订单ID作为幂等键,设置成功说明是第一次处理 # nx=True 表示只有键不存在时才写入,ex=60 表示标记60秒后自动过期 ok = r.set(f"order:{order_id}:processed", "1", nx=True, ex=60) if not ok: print(f"重复消息,订单 {order_id} 已处理过,直接跳过") return # 第一次处理,执行业务逻辑 process_order(order_id) print(f"订单 {order_id} 处理完成")

这里有个细节要注意:SETNX和后面的业务处理不是原子的,也就是说"设置标记成功"和"业务处理成功"之间如果程序崩溃了,标记已经存在,业务却没做完,这条消息就会被永久跳过。严格的做法是把"设置标记"和"业务处理"放到一个事务里,或者用状态机先更新状态再处理。入门阶段先用上面的写法没问题,但要清楚它的边界。

7.4 "前端点两次"问题的最终答案

回到开头那个问题。一个生产级系统要真正防住"前端点两次",需要在四个层面同时设防:

  1. 前端层:按钮点击后立即禁用,或者加一个提交锁。代码示例:
let locked = false; submitBtn.onclick = async function () { if (locked) return; locked = true; try { await sendOrder(); } finally { locked = false; } };
  1. 后端接口层:生成或接收一个业务请求ID(比如订单号),在接口入口处做一次查重。

  2. MQ消费层:消费消息时用业务ID做幂等判断,确保重复消息不产生重复业务。

  3. 存储层:数据库唯一索引作为最后一道兜底防线。

只有把这四层都做好,才能说你的系统真正解决了"点两次"的问题。这也正是MQ幂等问题的完整答案:业务ID贯穿全链路,每一层都拿它做去重,缺一环都可能出事。

8. 入门避坑清单与学习路径建议

8.1 我踩过的一些坑

讲几个我自己入门时踩过的坑,希望能帮你省点时间。

第一个坑:直接连开发环境的MQ。我刚开始学习时,图省事直接把电脑上的Python脚本指向公司的开发环境RabbitMQ,结果连接总是失败。后来发现是公司网络隔离,本地根本访问不到。建议学习阶段全部走本机Docker,干净又可控。

第二个坑:只声明队列不设置持久化参数。我用RabbitMQ做的第一个Demo,跑得很开心,结果某个周末Docker容器重启,队列和消息全没了。RabbitMq的队列默认不是持久化的,必须显式设置durable=True,发布时还要设置delivery_mode=2,两者缺一不可。

第三个坑:测试时开着好几个消费者,以为消息丢了。其实消息被其他消费者消费了,只是我没意识到同一队列的消息会被多个消费者瓜分。后来我在管理界面看到每个队列的消费者连接数,才反应过来。这个特性本身很有用,但如果你期望某个消费者收到"所有"消息,一定要理解它的分发机制。

第四个坑:把MQ当数据库用。有段时间我为了让一个统计功能实现起来简单,把很多业务中间状态都塞进MQ,结果MQ里积压了大量消息,重启后消费不过来,反而影响了正常的订单消息。MQ适合传递"事件通知"和"待办任务",不适合做业务数据的持久化存储。

8.2 建议的学习路线

以我带过几个新人的经验,入门MQ可以按这个节奏走:

  • 第一阶段:用Docker把RabbitMQ跑起来,跑通生产和消费,在管理界面观察队列状态变化。目标:理解Producer、Broker、Consumer三个角色。
  • 第二阶段:研究ACK确认、消息持久化、手动确认与自动确认的区别。目标:理解"至少一次"语义和重复消费的产生原因。
  • 第三阶段:模拟一个真实业务,比如"用户下单 → 发MQ → 扣库存+发短信",在消费者里加上Redis幂等判断。目标:吃透幂等设计。
  • 第四阶段:换Kafka入门,学习Topic、Partition、Consumer Group这些概念。你会发现,有了RabbitMQ的基础,Kafka真的不难。

最后再分享一个我个人的习惯:学习MQ时,不要只盯着官方文档,一定要自己在管理界面里多点点看。发一条消息,观察它怎么进队列、怎么被消费、ACK之后消息去了哪里;故意注释掉ACK,看看消息会不会重新投递。这些"亲眼所见"的经验,比背十遍概念都有用。把基础打牢,后面无论是做订单系统、秒杀系统还是日志平台,你都会感谢当初花时间把MQ的这套机制彻底搞明白了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询