使用MassTransit在LocalStack发布消息时遭遇SNS错误,Publish调用无响应
从你描述的情况来看,问题的核心很明确:简单消息能正常发布消费,但包含CaseContextDto的AddContextMessage会卡住,且LocalStack日志抛出SNS批量发布的空值处理错误。结合你提到的自定义Json转换器和MassTransit调试时卡在TaskExecutor.Push()的现象,我整理了几个针对性的排查和解决方向:
1. 先排查自定义Json转换器的序列化输出
因为只有带CaseContextDto的消息出问题,而它依赖自定义转换器,大概率是序列化后的消息体里存在LocalStack SNS无法处理的结构(比如null集合/数组)。LocalStack的SNS实现是Python写的,日志里的object of type 'NoneType' has no len()错误,就是Python代码尝试对null值调用len()导致的。
你可以在Publish前手动序列化消息,看看输出是否符合预期:
using (var scope = _scopeFactory.CreateScope()) { var publishEndpoint = scope.ServiceProvider.GetRequiredService<IPublishEndpoint>(); // 手动序列化消息,用和MassTransit相同的配置 var settings = scope.ServiceProvider.GetRequiredService<IJsonSettingsManager>().SystemTextSettings; var json = JsonSerializer.Serialize(message, settings); _logger.LogInformation("Serialized message content: {JsonContent}", json); await publishEndpoint.Publish(message); }
重点检查CaseContext里的集合、数组类型字段,是否被序列化成了null而不是空数组/集合——这是最容易触发LocalStack这个bug的场景。
2. 调整Json序列化配置,避免空值引发的问题
如果发现序列化结果里有null集合,修改MassTransit的Json配置,强制集合类型即使为空也序列化为空结构,而不是null:
x.UsingAmazonSqs( (context, cfg) => { var settings = context.GetRequiredService<IJsonSettingsManager>().SystemTextSettings; cfg.ConfigureJsonSerializerOptions(options => { foreach (var converter in settings.Converters) { options.Converters.Add(converter); } // 新增:当字段为null时不序列化,避免集合类型输出null options.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; // 可选:关闭缩进,减少消息体积,避免不必要的格式问题 options.WriteIndented = false; return options; }); // 其他配置保持不变... } );
同时检查你的自定义转换器,确保它们在处理空对象/空集合时,返回的是合法的JSON结构(比如空数组[]),而不是null。
3. 升级LocalStack到最新版本
LocalStack的旧版本确实存在不少SNS/SQS的空值处理bug,尤其是批量发布场景。你当前遇到的NoneType has no len()错误,很可能已经在新版本中被修复了。
可以用docker命令升级LocalStack:
docker pull localstack/localstack:latest
然后重启LocalStack,重新创建队列和订阅,再测试发布消息。
4. 临时绕过MassTransit的批量发布机制
如果升级LocalStack后问题依旧,可以尝试强制MassTransit对这条消息使用单条发布,而不是批量发布:
x.UsingAmazonSqs( (context, cfg) => { // 针对AddContextMessage禁用批量发布 cfg.Publish<AddContextMessage>(p => { p.UseMessageBatch(false); }); // 其他配置保持不变... } );
如果这样能正常发布,说明问题确实出在LocalStack的批量发布处理逻辑上。
5. 检查ScopedTopics的配置冲突
你在配置里启用了h.EnableScopedTopics()和h.Scope(configuration.GetDeploymentName()),虽然队列和订阅看起来正常,但有时候Scope命名和Topic命名的冲突也可能导致奇怪的问题。可以临时去掉Scope配置,测试是否能正常发布消息:
// 注释掉这两行测试 // h.Scope(configuration.GetDeploymentName()); // h.EnableScopedTopics();
总结下来,最可能的原因是自定义Json转换器导致消息体出现了LocalStack SNS无法处理的null集合,或者LocalStack版本过低存在bug。先从序列化输出排查,再逐步调整配置或升级LocalStack,应该能解决问题。
备注:内容来源于stack exchange,提问作者ScottishTapWater

