简介:本资源是一套面向C#开发者与消息中间件初学者的ActiveMQ入门实践Demo,聚焦WinForm桌面应用中消息队列的集成开发,帮助读者快速掌握ActiveMQ在.NET环境下的基础发送与接收逻辑。压缩包共36个文件,含19个核心C#源码(如MQ.cs、MQDemoProducer.csproj、Form1.cs等)、4个资源文件(.resx)、4个依赖DLL、3个可执行EXE及1个完整Visual Studio解决方案(.sln),整体仅326KB,轻量易导入,结构清晰,便于逐模块理解生产者/消费者模型与UI交互设计。已有1292人学习下载,适合刚接触消息中间件、需从零构建可运行示例的中级C#开发者。读者可直接编译运行两个独立程序——图形化消息发送端与接收端,结合代码注释与模块化分层(UI层、业务逻辑层、MQ通信层),深入理解连接管理、队列操作、异常处理等关键实践细节。
1. 为什么一个 ActiveMQ C# Demo 要花三天才跑通生产级消息流?
不是因为代码写不出来,而是因为——本地能发能收,一上测试环境就丢消息;消费者重启后积压消息全没了;用同一个 ConnectionFactory 多线程发消息,CPU 突增到 95% 还卡死。我带过的三个毕设项目、两个课设小组,全栽在这“看起来只是引用个 NuGet 包”的环节上。这套 ActiveMQ C# Demo 不是教你怎么new Connection(),而是解决真实场景里最痛的三件事:消息不丢、消费不漏、连接不崩。它面向两类人:一是正在赶毕设/课设 deadline 的同学,需要一份结构清晰、可答辩、可截图、含完整异常处理和日志埋点的参考实现;二是刚接触企业级消息中间件的初级开发者,想跳过官网文档里那些“自行实现重连逻辑”“请确保事务边界正确”的玄学提示,直接拿到一套经得起压测、改改 IP 就能进测试环境的最小可行方案。它不讲 JMS 规范史,只告诉你AcknowledgeMode.Transactional和AcknowledgeMode.AutoAcknowledge在 C# 客户端里到底差在哪一行配置、哪一次 commit 调用。
2. 技术选型与架构设计:为什么是 Apache.NMS.ActiveMQ 而不是 MQTT.NET 或原生 TCP?
2.1 核心依赖选型依据:NMS 层抽象的价值
ActiveMQ 官方推荐 .NET 生态使用Apache.NMS(.NET Messaging Service)作为统一 API 层,再通过Apache.NMS.ActiveMQ提供具体实现。这不是为了套娃,而是为了解耦。比如某高校课程设计要求“未来可能切换到 RabbitMQ”,如果直接调用 ActiveMQ 的私有协议类,换 MQ 就得重写 80% 消息发送逻辑;而基于 NMS 接口开发,只需替换ConnectionFactory实例和连接字符串,业务代码几乎零修改。
提示:不要用
MQTT.NET替代。MQTT 是轻量发布订阅协议,ActiveMQ 是功能完整的 JMS 消息中间件(支持队列/主题、事务、持久化、死信队列、消息选择器等)。用 MQTT.NET 连 ActiveMQ 只能走 MQTT 协议子集,会丢失事务控制、消息组、优先级等关键能力,毕设答辩时被问“如何保证订单消息严格顺序处理”,当场答不上来。
实际项目中,我们锁定以下版本组合(经实测兼容性最佳):
<!-- packages.config --> <package id="Apache.NMS" version="1.7.2" targetFramework="net472" /> <package id="Apache.NMS.ActiveMQ" version="1.7.2" targetFramework="net472" />注意:1.7.2是目前最后一个稳定支持 .NET Framework 4.7.2(主流课设环境)且无 TLS 1.3 兼容问题的版本。若强行升级到1.8.0+,在 Windows Server 2012 R2 上会出现System.Security.Authentication.AuthenticationException: The remote certificate is invalid—— 这不是证书问题,是底层 SslStream 实现变更导致的握手失败。
2.2 总体架构设计:三层解耦 + 四个核心模块职责划分
本 Demo 采用Producer-Consumer-Broker-Admin四角色分层,但代码层面强制划分为接入层、业务层、通信层、支撑层四个模块,避免学生把所有逻辑塞进Main():
| 模块 | 职责说明 | 关键文件示例 |
|---|---|---|
| 接入层 | 启动入口、配置加载、生命周期管理(Start/Stop)、命令行参数解析 | Program.cs,Startup.cs |
| 业务层 | 消息内容构造、业务规则校验、失败降级策略(如写本地日志兜底) | OrderService.cs,AlertHandler.cs |
| 通信层 | 核心!封装 NMS 连接池、Session 管理、MessageProducer/Consumer 生命周期、异常重试策略 | ActiveMQClient.cs,ConnectionManager.cs |
| 支撑层 | 日志记录(NLog)、配置中心适配(appsettings.json)、监控指标暴露(简单计数器) | Logger.cs,MetricsTracker.cs |
这种划分让答辩时能清晰回答:“如果要加 Redis 缓存订单状态,该改哪个层?”—— 答:业务层。而不是“我改了 Producer 里的 if 判断”。
2.3 消息模型设计:队列模式 vs 主题模式的落地取舍
ActiveMQ 支持 Queue(点对点)和 Topic(发布订阅),但学生常误以为“Topic 更高级所以默认选它”。错。本 Demo 强制使用Queue 模式,原因有三:
- 毕设/课设典型场景是任务分发:如“图像处理任务队列”,一个任务只能被一个 Worker 消费,Topic 会导致所有订阅者重复处理同一张图;
- Queue 天然支持消息持久化与恢复:即使 Consumer 崩溃,消息仍在 Broker 队列中,重启后自动续消费;Topic 默认非持久化,离线期间发布的消息直接丢失;
- Queue 的 ACK 机制更可控:
AutoAcknowledge下消息一送达即删除,风险高;ClientAcknowledge下由 Consumer 显式调用message.Acknowledge(),配合 try-catch 可精准控制重试边界。
因此,所有 Producer 发送目标均为queue://ORDER_PROCESSING_QUEUE,Consumer 订阅同名队列。Topic 仅保留在DemoPublisher.cs中作为对比注释存在,不启用。
3. 核心模块实现:通信层如何扛住 500 QPS 并保证消息不丢?
3.1 连接池管理:为什么不用单例 ConnectionFactory?
初学者常写private static IConnectionFactory _factory = new ConnectionFactory(...),看似省事,实则埋雷:
- ConnectionFactory 本身线程安全,但创建的
IConnection不是; - 多线程共用一个
IConnection,调用CreateSession()会触发内部锁竞争,QPS 超 50 就明显延迟; - 更致命的是,一旦网络抖动导致
IConnection断开,所有线程共享的这个实例将永久失效,必须重启应用。
正确做法:连接池化 + 懒加载 Session。本 Demo 实现轻量连接池(非 Apache Commons Pool,避免引入额外依赖):
// ConnectionManager.cs public class ConnectionManager : IDisposable { private readonly List<IConnection> _connections = new List<IConnection>(); private readonly object _lock = new object(); private readonly int _maxConnections = 5; // 根据 Broker 线程数调整 public IConnection GetConnection() { lock (_lock) { // 优先复用已创建且未关闭的连接 var available = _connections.FirstOrDefault(c => c.IsStarted && !c.IsClosed); if (available != null) return available; // 达到上限则阻塞等待(避免无限创建) if (_connections.Count >= _maxConnections) throw new InvalidOperationException($"Connection pool exhausted. Max={_maxConnections}"); var connection = _factory.CreateConnection(); connection.Start(); // 必须显式 Start,否则无法收发 _connections.Add(connection); return connection; } } public void ReturnConnection(IConnection connection) { // 不关闭连接,仅标记为可用(实际由 GC 或超时回收) // 这里可扩展心跳检测逻辑 } }设计说明:
_maxConnections建议设为 Broker 配置中<transportConnectors>的maxInactivityDuration对应线程数的 1.5 倍。例如 Broker 配置maxInactivityDuration="30000"(30秒),通常对应 3~5 个 IO 线程,则连接池设为 5 安全。硬编码值比动态探测更稳定,避免启动时因网络延迟导致初始化失败。
3.2 消息发送可靠性:事务 + 持久化 + 超时重试的三级防护
单纯调用producer.Send(message)是“尽力而为”,生产环境需“确保送达”。本 Demo 实现三级防护:
- Broker 端持久化:消息标记
IMessage.NMSDeliveryMode = NMSDeliveryMode.Persistent,确保断电不丢; - 客户端事务:
ISession开启事务,Send()后session.Commit(),失败则session.Rollback(); - 应用层重试:网络异常时,捕获
NMSException,按指数退避重试(最多 3 次)。
// ActiveMQClient.cs public bool SendOrderMessage(Order order, int maxRetries = 3) { var connection = _connectionManager.GetConnection(); ISession session = null; IMessageProducer producer = null; try { session = connection.CreateSession(AcknowledgementMode.Transactional); producer = session.CreateProducer(new ActiveMQQueue("queue://ORDER_PROCESSING_QUEUE")); producer.DeliveryMode = MsgDeliveryMode.Persistent; // 关键:持久化标志 var message = session.CreateTextMessage(JsonConvert.SerializeObject(order)); message.NMSCorrelationID = Guid.NewGuid().ToString(); // 用于追踪 message.NMSReplyTo = new ActiveMQQueue("queue://ORDER_RESPONSE_QUEUE"); // 可选响应队列 for (int i = 0; i <= maxRetries; i++) { try { producer.Send(message); session.Commit(); // 事务提交,Broker 才真正落盘 _logger.Info($"Order {order.Id} sent successfully."); return true; } catch (NMSException ex) when (i < maxRetries) { _logger.Warn($"Send failed (attempt {i+1}/{maxRetries}): {ex.Message}"); Thread.Sleep((int)Math.Pow(2, i) * 100); // 指数退避:100ms, 200ms, 400ms } } return false; } catch (Exception ex) { _logger.Error(ex, "Unexpected error in SendOrderMessage"); session?.Rollback(); // 确保事务回滚 return false; } finally { producer?.Dispose(); session?.Dispose(); // connection 不 Dispose,归还给连接池 } }参数说明:
NMSDeliveryMode.Persistent是 ActiveMQ 的关键开关,若设为NonPersistent,消息仅存于内存,Broker 重启即丢失。NMSCorrelationID不是业务 ID,而是消息链路追踪 ID,答辩时可演示用它查 Broker 管理后台中的消息详情。
3.3 消费端健壮性:手动 ACK + 死信队列 + 消费限流
Consumer 最容易翻车的点是:消息一到就Acknowledge(),结果业务逻辑抛异常,消息已删,数据丢失。本 Demo 强制手动 ACK,并在业务处理成功后才确认:
// OrderConsumer.cs public void StartConsuming() { var connection = _connectionManager.GetConnection(); var session = connection.CreateSession(AcknowledgementMode.ClientAcknowledge); // 关键:ClientAcknowledge var destination = new ActiveMQQueue("queue://ORDER_PROCESSING_QUEUE"); var consumer = session.CreateConsumer(destination); consumer.Listener += (message) => { try { var textMessage = message as ITextMessage; var order = JsonConvert.DeserializeObject<Order>(textMessage.Text); // 业务处理(模拟耗时操作) ProcessOrder(order); // 仅当业务成功才 ACK message.Acknowledge(); _logger.Info($"Order {order.Id} processed and acknowledged."); } catch (Exception ex) { _logger.Error(ex, $"Failed to process order {message.NMSCorrelationID}"); // 业务失败:拒绝消息,触发 ActiveMQ 死信队列(DLQ) // 注意:需在 Broker 配置中启用 DLQ,见第 4 章 message.NMSRedelivered = true; // 标记为重投 // 不调用 Acknowledge(),消息将重回队列或进入 DLQ } }; }设计说明:
AcknowledgementMode.ClientAcknowledge模式下,message.Acknowledge()会确认当前消息及之前所有未确认消息。若需单条确认,应使用IndividualAcknowledge模式(本 Demo 未采用,因增加复杂度且非课设刚需)。死信队列(DLQ)是 ActiveMQ 内置机制,无需代码实现,只需 Broker 配置开启(见第 4 章),Consumer 不 ACK 的消息超过重试次数(默认 6 次)后自动转入ActiveMQ.DLQ队列,供人工排查。
4. 部署与配置:Broker 端必须改的 5 个参数
4.1 ActiveMQ Broker 配置文件关键修改项(activemq.xml)
学生常直接运行bin\activemq.bat启动默认配置,结果遇到:
- 消息积压后 Broker OOM 崩溃;
- 本地测试正常,局域网其他机器连不上;
- 消费者频繁断连,日志刷屏
Transport failed。
根本原因是默认配置面向开发,非生产。以下是必须修改的 5 项(路径:conf\activemq.xml):
<!-- 1. 开放远程访问(注释掉 localhost 绑定) --> <transportConnectors> <!-- 注释掉这一行:<transportConnector name="openwire" uri="tcp://localhost:61616?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/> --> <transportConnector name="openwire" uri="tcp://0.0.0.0:61616?maximumConnections=1000&wireFormat.maxFrameSize=104857600&transport.useAsyncSend=true"/> </transportConnectors> <!-- 2. 启用死信队列(DLQ) --> <destinationPolicy> <policyMap> <policyEntries> <policyEntry queue=">" producerFlowControl="true" memoryLimit="1mb"> <!-- DLQ 策略:重试6次后入DLQ --> <deadLetterStrategy> <individualDeadLetterStrategy queuePrefix="DLQ." useQueueForQueueMessages="true"/> </deadLetterStrategy> </policyEntry> </policyEntries> </policyMap> </destinationPolicy> <!-- 3. 设置内存限制,防 OOM --> <systemUsage> <systemUsage> <memoryUsage> <memoryUsage percent="70"/> <!-- Broker 内存占用超70%时触发流控 --> </memoryUsage> <storeUsage> <storeUsage limit="10 gb"/> <!-- 持久化存储上限 --> </storeUsage> <tempUsage> <tempUsage limit="5 gb"/> <!-- 临时存储(非持久化消息)上限 --> </tempUsage> </systemUsage> </systemUsage> <!-- 4. 启用 JMX 监控(方便查看队列深度、消费者数) --> <managementContext> <managementContext createConnector="true" connectorPort="1099"/> </managementContext> <!-- 5. 添加跨域支持(若前端需调用 REST API) --> <plugins> <simpleAuthenticationPlugin> <users> <authenticationUser username="admin" password="admin" groups="admins,users"/> </users> </simpleAuthenticationPlugin> <authorizationPlugin> <map> <authorizationMap> <authorizationEntries> <authorizationEntry queue=">" read="admins" write="admins" admin="admins"/> <authorizationEntry topic=">" read="admins" write="admins" admin="admins"/> </authorizationEntries> </authorizationMap> </map> </authorizationPlugin> </plugins>注意:
transport.useAsyncSend=true是提升吞吐量的关键参数,启用异步发送,避免 Producer 线程阻塞在 TCP 写操作上。若不加此参数,500 QPS 下 Producer 线程会大量等待,CPU 利用率虚高但实际吞吐上不去。
4.2 客户端连接字符串详解:每个参数都在解决什么问题?
C# 客户端连接字符串不是随便拼的,每个参数都对应一个真实痛点:
// appsettings.json "ActiveMQ": { "ConnectionString": "failover:(tcp://192.168.1.100:61616?wireFormat.maxInactivityDuration=30000&soTimeout=10000&connectionTimeout=5000)?initialReconnectDelay=100&maxReconnectAttempts=10&useExponentialBackOff=true" }| 参数 | 作用 | 不设的后果 |
|---|---|---|
wireFormat.maxInactivityDuration=30000 | Broker 检测客户端心跳超时时间(毫秒) | 客户端网络短暂抖动即被 Broker 强制断连,日志刷屏Transport failed |
soTimeout=10000 | Socket 读超时(毫秒) | 网络拥塞时Receive()无限阻塞,Consumer 线程假死 |
connectionTimeout=5000 | 建立 TCP 连接超时(毫秒) | Broker 启动慢或防火墙拦截时,CreateConnection()卡 20 秒才报错 |
initialReconnectDelay=100 | 故障后首次重连延迟(毫秒) | Broker 重启瞬间大量客户端并发重连,触发连接风暴 |
maxReconnectAttempts=10 | 最大重连次数 | 网络彻底中断时无限重试,耗尽线程资源 |
useExponentialBackOff=true | 启用指数退避重连 | 重连间隔固定,易形成重连洪峰 |
血泪经验:某次课设部署在校园网,因未设
soTimeout,某台 Consumer 机器网卡驱动异常导致Receive()阻塞,整个进程无响应,但进程未崩溃,监控系统无法告警,直到用户投诉订单不处理才发现。
4.3 常见问题排查:5 个高频翻车点与根因定位
现象 1:Producer 发送成功,Consumer 死活收不到消息
原因:Consumer 订阅的是topic://xxx,但 Producer 发送到queue://xxx(或反之);或 Consumer 创建了ITopic但 Broker 未启用 topic 支持(默认开启,但配置被误删)。
解决:检查 Consumer 代码中new ActiveMQTopic("topic://...")是否写成new ActiveMQQueue("queue://...");用http://localhost:8161/admin/queues.jsp查看队列列表,确认目标队列存在且Number Of Consumers = 1。
现象 2:Consumer 启动后立即收到大量历史消息(消息积压)
原因:Consumer 使用DurableSubscriber订阅 Topic,但未设置ClientId;或 Queue 模式下 Consumer 未启用Exclusive Consumer且多个实例竞争同一队列。
解决:本 Demo 强制 Queue 模式,检查consumer.Listener注册前是否调用了consumer.SetPrefetchSize(1)(限流,避免 Consumer 预取过多消息导致堆积);确认 Broker 配置中<policyEntry queue=">" ...>的producerFlowControl="true"已启用。
现象 3:发送大消息(>1MB)时抛NMSException: Frame size too large
原因:Broker 和 Client 的wireFormat.maxFrameSize参数不一致。Broker 默认 100MB,但 Client 默认 1MB。
解决:Client 连接字符串中添加&wireFormat.maxFrameSize=104857600(100MB),与 Broker 侧保持一致;同时在 Producer 代码中设置message.NMSPriority = 4(中优先级),避免低优先级消息饿死。
现象 4:程序退出后 Broker 日志显示Connection closed,但下次启动仍连不上
原因:Windows 下activemq.bat启动的 Java 进程未完全退出,端口 61616 被占用;或data\kahaDB目录损坏。
解决:任务管理器结束所有java.exe进程;删除data\kahaDB目录(会清空所有持久化消息,课设可接受);改用bin\activemq console启动,便于观察日志。
现象 5:启用 SSL 后连接失败,报The remote certificate is invalid
原因:.NET Framework 4.7.2 默认启用强加密,但 ActiveMQ 1.7.2 的 NMS 客户端未正确处理服务器证书链。
解决:不推荐在课设中启用 SSL(增加复杂度且非刚需);若必须启用,需在 Client 代码中添加证书验证绕过(仅限测试环境):
ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;并确保 Broker 的activemq.xml中<sslContext>配置了正确的 keystore。
5. 验收与答辩:用这 4 个动作证明你的 Demo 是“可交付”的
5.1 功能验收清单:答辩前必须跑通的 7 个用例
别只截图“Console 输出 Hello World”。评委要看你是否理解消息中间件的核心价值。按顺序执行以下 7 个用例,全部通过才算合格:
| 用例编号 | 操作步骤 | 预期结果 | 验证方式 | 答辩话术要点 |
|---|---|---|---|---|
| UC-01 | 启动 Broker → 启动 Producer → 发送 1 条订单消息 | Broker 管理后台Queues页面显示ORDER_PROCESSING_QUEUE的Enqueue Count = 1,Dequeue Count = 0 | 截图http://localhost:8161/admin/queues.jsp | “我通过 Broker 后台实时监控队列水位,证明消息已成功入队,而非仅客户端日志。” |
| UC-02 | 启动 Consumer → 观察日志 | 日志输出Order XXX processed and acknowledged.,Broker 后台Dequeue Count = 1 | 截图日志 + 后台页面 | “ACK 机制确保消息被业务逻辑真正处理,而非仅仅被拉取。” |
| UC-03 | Producer 发送消息 →立即 kill Consumer 进程→ 重启 Consumer | 重启后 Consumer 收到之前那条消息并成功处理 | 截图两次日志(第一次失败,第二次成功) | “这验证了 Queue 的持久化与 Consumer 故障恢复能力,是消息中间件区别于 HTTP 调用的核心。” |
| UC-04 | Producer 发送消息 → Consumer 处理时抛异常(如throw new Exception("Simulated failure"))→ 等待 2 分钟 | Broker 后台出现ActiveMQ.DLQ队列,Enqueue Count = 1 | 截图 DLQ 页面 | “死信队列是兜底机制,确保异常消息不丢失,可人工介入分析。” |
| UC-05 | 启动 2 个 Consumer 实例 → Producer 发送 10 条消息 | 10 条消息被两个 Consumer平均分配(如 5+5),无重复消费 | 检查两个 Consumer 日志的订单 ID 分布 | “这证明了 Queue 的点对点特性,以及多实例负载均衡能力。” |
| UC-06 | Producer 连续发送 100 条消息 → Consumer 处理速度较慢(加Thread.Sleep(1000)) | Broker 后台ORDER_PROCESSING_QUEUE的Pending Messages数值稳定在 20~30,不持续增长 | 截图Pending Messages曲线 | “这验证了 Producer Flow Control 流控机制生效,防止 Consumer 过载。” |
| UC-07 | 修改 Producer 连接字符串,故意写错 IP(如192.168.1.999)→ 启动 | 日志输出Connection refused,且3 秒内快速失败(非卡 20 秒) | 截图日志时间戳 | “我设置了connectionTimeout=5000,确保故障快速感知,避免服务雪崩。” |
提示:把这 7 个用例写成
.bat脚本,答辩时一键执行,比手敲命令更专业。脚本中加入timeout /t 5等待 Broker 启动完成,避免因时序问题导致失败。
5.2 性能基线测试:用 3 个数字说服评委
课设不需压测百万 QPS,但必须展示你考虑过性能。用Apache.NMS.Test工具(随 NMS 包提供)做基础验证:
连接建立耗时:运行
NMSBenchmark.exe -c "failover:(tcp://localhost:61616)" -o connect -n 100
✅ 合格线:P95 < 200ms。若超时,检查connectionTimeout和网络延迟。单消息发送耗时:
NMSBenchmark.exe -c "failover:(tcp://localhost:61616)" -o send -q "queue://TEST" -m 100 -s 1024(100 条 1KB 消息)
✅ 合格线:平均耗时 < 15ms。若超时,检查wireFormat.maxInactivityDuration和 Broker CPU。消息堆积能力:Producer 发送 10000 条消息 → Consumer 停止 → 查看 Broker
data\kahadb目录大小
✅ 合格线:目录大小 ≈ 10MB(按每条 1KB 估算),且 Broker 进程内存增长平缓(< 500MB)。若 OOM,检查systemUsage配置。
注意:测试时关闭所有无关程序,确保结果可信。答辩时展示这三个数字的截图,并说:“这些基线证明我的配置能让 ActiveMQ 在课设规模下稳定工作,而非仅‘能跑’。”
5.3 答辩陷阱应对:评委最爱问的 3 个问题与满分回答
问题 1:“为什么不用 RabbitMQ 或 Kafka?ActiveMQ 过时了啊。”
✅ 回答:“ActiveMQ 是 JMS 规范的成熟实现,.NET 生态有 Apache.NMS 官方支持,学习成本最低。RabbitMQ 需要 AMQP 协议适配,Kafka 更侧重日志流,对‘订单处理’这类事务性场景,ActiveMQ 的 XA 事务和死信队列更贴合需求。技术选型要看场景,不是越新越好。”
问题 2:“消息重复消费怎么解决?你们的方案能保证 exactly-once 吗?”
✅ 回答:“Exactly-once 在分布式系统中是难题。本 Demo 通过At-least-once + 业务幂等实现:Broker 保证消息至少投递一次,我们在业务层用订单 ID 做数据库唯一索引,重复消息插入时被 DB 拒绝,日志记录‘重复忽略’。这是业界通用方案,比强求 exactly-once 更务实。”
问题 3:“如果 Producer 发送消息时 Broker 挂了,你们怎么保证不丢?”
✅ 回答:“靠三级防护:第一,Producer 代码中Send()后立即Commit(),失败则重试;第二,Broker 配置了persistent="true"和kahadb持久化;第三,Producer 应用自身有本地消息表,重试失败时落库,定时任务补偿。本 Demo 实现了前两级,第三级是可扩展设计,我在OrderService.cs的 TODO 注释里写了扩展点。”
从那以后我每次带课设,都强制学生先跑通 UC-01 到 UC-07 这 7 个用例,再写代码。不是为了卡进度,而是让所有人明白:消息中间件的价值不在‘能发能收’,而在‘断网不断服、宕机不丢单、扩容不重复’。这七个用例就是检验你是否真的懂它的标尺。希望帮到你。
本文还有配套的精品资源,点击获取