NServiceBus超时/调度立即触发问题求助(关联Azure存储队列包)
最近我碰到了一个非常奇怪的NServiceBus调度问题:不管我指定哪个UTC时间作为超时触发点,Saga的超时都会立即触发,还进入无限循环;就连NServiceBus内置的ScheduleEvery方法也变得疯狂,每秒触发数百次任务。折腾了好久,终于找到问题根源,这里把排查过程和解决方案分享出来。
我的场景
- 使用NServiceBus 6.4.3,自托管端点,采用
InMemoryPersistence - 实现了一个调度Saga,通过
RequestTimeout设置UTC时间触发任务,完成后循环调度次日的超时 - 同一进程中托管了两个端点:一个处理MSMQ消息,另一个原本计划对接Azure存储队列(但实际上还没配置使用)
- 只要项目引用了
NServiceBus.Azure.Transports.WindowsAzureStorageQueues包,哪怕完全没用到这个传输,问题就会出现
问题根源
经过反复测试和排查,发现问题出在同一进程中同时存在MSMQ传输和Azure存储队列传输的组件。哪怕你没有实际配置Azure存储队列传输,只要引用了对应的NuGet包,相关的底层组件就会被加载,这会干扰NServiceBus的超时调度时钟逻辑,导致超时判断失效——它会错误地认为所有设置的超时时间都已经过期,所以一启动就触发超时,然后因为循环调度又立即生成新的超时,最终进入无限循环。
解决办法
针对这个问题,我整理了几个可行的解决方案:
1. 拆分端点到独立进程
把MSMQ端点和Azure存储队列端点分别部署到不同的进程里,彻底避免两种传输组件在同一进程中共存。这是最彻底的解决方式,能从根源上消除组件冲突的可能。
2. 移除未使用的Azure存储队列传输包
如果当前并没有实际使用Azure存储队列传输,直接卸载NServiceBus.Azure.Transports.WindowsAzureStorageQueues这个NuGet包。卸载后,冲突的组件不会被加载,NServiceBus的超时调度逻辑就能恢复正常。我自己就是用这个办法解决的,测试后Saga的超时和ScheduleEvery都能按预期工作了。
3. 显式隔离端点配置(必须保留多传输的场景)
如果因为业务需求必须在同一进程中保留两种传输的引用,那就要确保每个端点的配置完全隔离,各自使用对应的传输和持久化:
- 为每个端点单独创建
EndpointConfiguration实例 - 显式指定每个端点的传输类型,避免默认配置被污染
- 针对超时持久化,也可以显式指定存储类型,比如:
// 为MSMQ端点配置超时持久化 msmqEndpointConfig.UsePersistence<InMemoryPersistence, StorageType.Timeouts>(); // 为Azure存储队列端点配置对应的持久化 azureEndpointConfig.UsePersistence<AzureStoragePersistence, StorageType.Timeouts>();
通过这种方式,让两个端点的调度逻辑各自独立,避免互相干扰。
内容的提问来源于stack exchange,提问作者sp1nakr

