BasicConsume默认丢消息或卡死channel,是因为其同步事件回调若用async void会导致异常静默丢失、ack误标,或await阻塞线程使channel挂起;应改用AsyncEventingBasicConsumer或BasicGet+手动Ack。

为什么BasicConsume默认会丢消息或卡死channel
因为 BasicConsume 注册的是同步事件回调,而 C# 中的 async void 方法一旦抛异常就无法捕获,且不会等待 await 完成。结果是:消息被标记为已接收(ack),但实际处理失败;或者回调里用了 await 却没用 async void,导致线程阻塞、channel 被 hang 住。
- 不要写
consumer.Received += (model, ea) => { await Handle(ea.Body); };—— 编译都过不了 - 也不要写
async void回调 —— 异常静默丢失,channel 不再收新消息 - RabbitMQ.Client v6.0+ 推荐用
AsyncEventingBasicConsumer,它内部自动调度并传播异常 - v5.x 兼容方案:用
channel.BasicGet手动拉取消息 +Task.Run(() => Handle(body))调度,再手动channel.BasicAck
发送JSON消息时body为什么总是空或解析失败
根本原因在于 RabbitMQ 不解析消息内容,IBasicProperties.ContentType 只是元数据标签,不触发自动序列化/反序列化。你传进去什么字节,对方就收到什么字节 —— 没有“智能识别JSON”这回事。
- 发送端必须显式序列化:
Encoding.UTF8.GetBytes(JsonSerializer.Serialize(obj)),别依赖Formatter或ContentType - 接收端必须显式反序列化:
JsonSerializer.Deserialize<MyDto>(Encoding.UTF8.GetString(body)) - 别设
basicProperties.ContentType = "application/json"就以为能自动转对象 —— 它只影响 HTTP 层或管理界面显示 - 如果用
System.Text.Json,注意JsonSerializerOptions.PropertyNamingPolicy两端要一致,否则字段映射失败
队列声明后消息还是发丢了?检查这三件事
channel.QueueDeclare 只保证队列存在,但不保证消息能持久化、不被丢弃。常见“发了却收不到”基本都卡在这几个配置上。
-
durable: true必须设,否则 RabbitMQ 重启后队列消失,新连接发的消息进不了队列 -
autoDelete: false必须设,否则最后一个消费者断开连接后队列被自动删掉 - 发送时
IBasicProperties.DeliveryMode = 2必须设,否则消息只是内存中暂存,broker 崩溃就丢 - 额外提醒:如果用的是 exchange + routingKey,还要确认
channel.ExchangeDeclare和channel.QueueBind都执行了,光声明队列没绑定等于没通路
连接字符串里localhost连不上?别忽略Windows防火墙和loopback限制
开发机上跑 RabbitMQ,默认 amqp://guest:guest@localhost:5672 看似没问题,但 Windows 10/11 后期版本对 loopback 连接加了限制,尤其启用了 Hyper-V 或 WSL2 后更常见。
- 先验证服务真在跑:
rabbitmqctl status,不是“服务启动了”就代表 AMQP 端口开着 - 检查
netstat -ano | findstr :5672,确认监听的是0.0.0.0:5672而非127.0.0.1:5672—— 后者可能被 loopback 白名单拦截 - 临时放行:以管理员身份运行
CheckNetIsolation LoopbackExempt -a -n="Microsoft.Win32WebViewHost"(适用于大多数 UWP/容器场景) - 生产环境别用 localhost —— 改用
127.0.0.1或真实 IP,避免 DNS 解析和策略干扰
RabbitMQ 的坑多数不在代码逻辑,而在 broker 配置、网络策略、序列化约定这些“边界地带”。哪怕 channel.BasicPublish 返回成功,也不能说明消息进了队列 —— 它只表示发到了 TCP 连接缓冲区。真要稳,得逐层确认:连接通、exchange 存在、queue 存在且绑定、delivery mode 设对、consumer 真在 running 且没被异常中断。


















