easynetq发布消息前必须确保消费者已就绪,首次bus.subscribe()才创建队列并绑定交换机;先发后订会导致消息被丢弃,除非显式声明持久化队列。

EasyNetQ发布消息前必须确保消费者已就绪
很多人一上来就跑 bus.Publish(),控制台没输出、消息“消失”了——其实不是丢了,是没人收。EasyNetQ的订阅是动态绑定队列的,**首次 bus.Subscribe() 时才会创建队列并绑定交换机**。如果先发后订,消息默认被丢弃(除非启用了持久化+队列存在且设置了 autoDelete=false)。
- 务必先启动 Consumer 程序,看到控制台打印 “Subscribed to …” 再运行 Publisher
- 开发阶段建议在 Consumer 中加
Console.ReadLine()防止进程退出,避免队列被自动删除 - 若需“先发后收”,得显式声明持久化队列:
bus.Advanced.QueueDeclare("my.queue", durable: true)
发送字符串或自定义对象?序列化是关键门槛
EasyNetQ 默认用 JSON 序列化,但如果你直接 bus.Publish("hello"),它会把字符串当成原始字节发出去,Consumer 收到的是 byte[],不是 string —— 这就是为什么常见报错 Cannot deserialize the current JSON array 或黑屏无反应。
- 发送简单值类型(如
string、int):显式指定泛型,bus.Publish<string>("hello")</string> - 发送自定义类(如
TextMessage):类必须有 public 无参构造函数,属性为 public get/set;发送时写bus.Publish(new TextMessage { Text = "hi" }) - 切勿手动
JsonConvert.SerializeObject()后再 Publish,EasyNetQ 会二次序列化导致嵌套
连接字符串写错、VirtualHost 权限不足,连不上就发不出
最常见的连接失败不是端口不通,而是 virtualHost 拼写错误或权限未分配。RabbitMQ 默认 virtual host 是 /,但 EasyNetQ 解析时会把 / 当作路径分隔符,必须 URL 编码为 %2F。
- 正确写法:
"host=localhost;virtualHost=%2F;username=guest;password=guest" - 如果用了非默认 vhost(如
myapp),先登录 http://localhost:15672 → Admin → Virtual Hosts → Add a new virtual host,再给用户分配权限 - 测试连接是否有效:在
RabbitHutch.CreateBus(...)后加一句bus?.Advanced.ExchangeDeclare("test.ex", "direct");,抛异常即说明连不上
异步发布要注意 Task 未 await 导致程序提前退出
bus.PublishAsync() 返回 Task,但很多示例里只写 .ContinueWith() 就不管了。主线程结束,进程退出,Task 根本没机会执行。
- 控制台程序中,改用
await bus.PublishAsync(msg),并在Main方法上加async(.NET Core 5+ 支持 async Main) - 若无法改 async Main,至少加
bus.PublishAsync(msg).Wait()强制同步等待(仅限开发调试,生产慎用) - 注意:PublishAsync 不保证消息已落盘,只表示已发到 RabbitMQ TCP 连接缓冲区;要确认投递成功,需启用 publisher confirms(需服务端配置 +
bus.Advanced.Publish底层调用)










