高并发下RabbitMQ应复用Connection、多用Channel,启用PublisherConfirms和持久化,显式声明durable队列与deliveryMode=2,CAP集成需手动开启确认与重试。
高并发下RabbitMQ连接不能每次都 new ConnectionFactory
直接在每次发消息时创建新连接,500 qps 就可能触发 socketexception: too many open files —— 这不是你的代码写错了,是操作系统对进程句柄数的硬限制被击穿了。
根本原因在于:RabbitMQ 的 Connection 是重量级对象(含 TCP 连接、心跳、认证通道),而 Channel 才是轻量级的、可复用的逻辑上下文。官方明确建议「一个连接复用多个 Channel」,且生产环境应控制每台应用实例的连接数 ≤ 5。
- 用
Singleton或IServiceProvider管理单个ConnectionFactory实例,而非每次 new - 每个业务线程/请求使用独立
IModel(即Channel),调用完 不Dispose(),让它自动归还到池中(.NET Client v6.8+ 支持ChannelPool) - 显式设置超时:
factory.AutomaticRecoveryEnabled = true+factory.NetworkRecoveryInterval = TimeSpan.FromSeconds(5),避免断连后卡死
PublisherConfirms 必须开,否则高并发=丢消息
默认情况下,IBasicPublish 调用返回即认为发送成功,但消息可能还在客户端缓冲区、网卡队列,甚至根本没进 RabbitMQ。压测时看似吞吐高,实则大量消息静默丢失——这在订单、支付类场景是致命的。
开启确认机制后延迟会升至 8–15ms(实测均值),但换来的是磁盘落盘级可靠性。这不是性能妥协,是必须支付的成本。
- 连接前必须设:
factory.PublisherConfirms = true - 发消息后立刻调用
channel.WaitForConfirmsOrDie(TimeSpan.FromSeconds(3)),超时就抛异常,由上层重试或降级 - 不要用
async/await等待确认——RabbitMQ .NET Client 的确认是同步阻塞模型,异步包装反而引入竞态
QueueDeclareAsync 参数错一个,高并发下镜像失效
你以为开了 HA 镜像队列就万无一失?如果声明队列时漏掉 durable: true 或 autoDelete: false,RabbitMQ 在节点重启后会直接删掉该队列定义,所有未消费消息永久消失。更隐蔽的是:镜像策略(比如 ha-mode: all)只对 durable 队列生效。
另外,exclusive 设为 true 在多消费者场景下会导致第二个消费者连不上——它不是“私有”,而是“独占连接”,和你想象的完全相反。
- 生产环境队列声明必须含:
await channel.QueueDeclareAsync(queue: "order.created", durable: true, exclusive: false, autoDelete: false) - 若需消息持久化,发布时务必设
deliveryMode: 2(IBasicProperties中),否则即使队列 durable,消息仍存在内存里 - 别信文档里“默认值安全”的说法——.NET Client v6.x 默认
durable=false,不显式写就是线上事故
CAP 框架集成时,UseRabbitMQ 默认不保证可靠性
很多团队用 CAP 做分布式事务,却在上线后发现「下单成功但积分没加」。查日志发现 CAP 的 UseRabbitMQ 扩展方法默认关闭 PublisherConfirms,且 RetryCount 默认为 0 —— 意味着网络抖动一次,消息就进死信队列,再也不会重试。
这不是 CAP 的 bug,是它把可靠性选择权交给了你。但多数人根本没意识到自己按了「跳过确认」按钮。
- 必须显式启用确认:
options.UseRabbitMQ(x => { x.Host = "localhost"; x.PublisherConfirms = true; }) - 设重试策略:
options.FailedRetryCount = 3,配合 CAP 自带的失败表做补偿 - 注意 CAP 的
ISubscriberService默认使用autoAck = true,要改成手动 Ack(model.BasicAck)才能配合重试逻辑
disc 不同步,所有镜像队列停摆。这事没法靠代码修复,得进容器里手动同步 /var/lib/rabbitmq/.erlang.cookie。


















