为何在EasyNetQ的SendAsync交叉调用中触发ArgumentNullException?
问题描述
使用EasyNetQ的Send/Receive结构连接两个服务时,首次消息可正常接收,但后续消息被丢弃至EasyNetQ_Default_Error_Queue,错误信息如下:
System.ArgumentNullException: routingKey must not be null (Parameter 'routingKey')
at EasyNetQ.Preconditions.CheckNotNull[T](T value, String name)
at EasyNetQ.Preconditions.CheckShortString(String value, String name)
at EasyNetQ.RabbitAdvancedBus.PublishAsync[T](Exchange exchange, String routingKey, Boolean mandatory, IMessage1 message, CancellationToken cancellationToken) at EasyNetQ.DefaultRpc.RespondToMessageAsync[TRequest,TResponse](Func3 responder, IMessage1 requestMessage, CancellationToken cancellationToken) at EasyNetQ.AdvancedBusExtensions.<>c__DisplayClass4_01.<b__0>d.MoveNext()
--- End of stack trace from previous location ---
at EasyNetQ.RabbitAdvancedBus.<>c__DisplayClass20_1.<b__7>d.MoveNext()
--- End of stack trace from previous location ---
at EasyNetQ.Consumer.HandlerRunner.InvokeUserMessageHandlerAsync(ConsumerExecutionContext context, CancellationToken cancellationToken)
该问题在后续每次调用中重复出现。
问题分析
从错误栈可定位核心问题:RPC响应发送时routingKey为null,触发EasyNetQ参数校验失败。虽然你使用的是Send/Receive模式,但错误栈中出现DefaultRpc.RespondToMessageAsync,说明消费端实际在处理RPC类型请求(可能代码误用了RPC相关消费方法),而响应所需的ReplyTo属性(对应routingKey)在后续请求中丢失,导致无法发送响应,最终消息被路由到错误队列。
解决方案
1. 修正消费模式匹配
如果是点对点消息传递(无需请求-响应):
- 确保消费端使用纯
Receive方法,不要在处理函数中调用RPC响应逻辑(比如不要返回值或尝试发送响应) - 检查发送端是否误将
Send与RPC的Request混用,导致消息携带ReplyTo属性但消费端无法处理
如果是RPC请求-响应场景:
- 替换为EasyNetQ原生
Request/Respond模式:- 发送端:用
bus.Request<TRequest, TResponse>(request)替代Send - 消费端:用
bus.Respond<TRequest, TResponse>(responder)替代Receive
该模式会自动维护ReplyTo属性传递,避免routingKey为空问题
- 发送端:用
2. 检查消息属性是否被篡改
排查消费端以下逻辑,确保未修改/清空原始消息的ReplyTo属性:
- 自定义消息拦截器(
IMessageInterceptor):OnConsume方法中不要修改message.Properties.ReplyTo - 自定义序列化器:确保序列化/反序列化过程保留消息元数据属性
- 业务处理代码:不要手动修改消息对象的属性字段
3. 升级EasyNetQ版本
部分旧版本EasyNetQ存在重复消费时ReplyTo属性丢失的bug,建议升级到最新稳定版(如v7.x及以上),修复已知属性传递问题
4. 验证队列配置
如果手动配置了RabbitMQ队列或绑定规则:
- 确保未设置丢弃消息元数据的参数
- 不要修改EasyNetQ默认生成的RPC相关队列/交换机配置
验证步骤
- 在消费端打印接收到的消息属性:
Console.WriteLine(message.Properties.ReplyTo);,确认首次和后续请求中该字段是否为空 - 临时移除自定义拦截器/序列化逻辑,测试是否还会出现错误
- 切换到纯
Request/Respond或纯Send/Receive模式,验证问题是否消失
内容的提问来源于stack exchange,提问作者Navid_pdp11

