PAYATHON 2026

Windows Services 中 NServiceBus subscription 不工作

数据阿明

结论

Subscriber 的 Bus Setup 中缺少 .LoadMessageHandlers()。

旧版 NServiceBus 需要先加载并扫描消息处理器,才能发现 IHandleMessages<T> 的实现,并根据 MessageEndpointMappings 自动向 Publisher 发送订阅请求。当前配置虽然启动了总线,但没有加载 handler,因此不会完成自动订阅,也看不到订阅成功的日志。

修正 Subscriber 配置

在 .UnicastBus() 后调用 .LoadMessageHandlers():

var bus = Configure.With()
    .Log4Net()
    .NinjectBuilder()
    .XmlSerializer()
    .MsmqSubscriptionStorage()
    .MsmqTransport()
    .IsTransactional(false)
    .PurgeOnStartup(false)
    .UnicastBus()
    .LoadMessageHandlers()
    .ImpersonateSender(false)
    .CreateBus()
    .Start();

还要确认 Subscriber 中确实存在对应事件的 handler,例如:

public class MyEventHandler : IHandleMessages<MyEvent>
{
    public void Handle(MyEvent message)
    {
        // 处理事件
    }
}

如果没有实现 IHandleMessages<MyEvent>,即使配置了 MessageEndpointMappings,NServiceBus 也找不到需要自动订阅的事件类型。

MessageEndpointMappings

Subscriber 需要声明事件由哪个 Publisher 发布,现有配置的方向没有问题:

<UnicastBusConfig>
  <MessageEndpointMappings>
    <add Messages="MyEventNamespace"
         Endpoint="publisher.input" />
  </MessageEndpointMappings>
</UnicastBusConfig>

如果 Publisher 位于另一台机器,端点中需要包含机器名:

<add Messages="MyEventNamespace"
     Endpoint="publisher.input@PublisherMachine" />

同时确认 Messages 指向事件类型所在的正确命名空间。某些旧版本也支持使用程序集限定的映射,具体格式取决于当前使用的 NServiceBus 版本。

Publisher 通常不需要为自己发布的事件配置 MessageEndpointMappings。这项配置主要用于告诉 Subscriber 应向哪个端点发送订阅请求。

还需要检查的项目

如果加入 .LoadMessageHandlers() 后仍未订阅,可以继续检查:

  • handler 所在的程序集是否已被 Configure.With() 扫描。如果 handler 位于单独的程序集,需要确保该程序集已经加载,必要时显式传给 Configure.With(...)。
  • 事件类型对 NServiceBus 而言是否确实属于消息。根据所用的旧版本,它通常需要实现相应的标记接口,例如:
public class MyEvent : IEvent
{
}
  • publisher.input 和 subscriber.input 队列是否存在,以及 Windows Service 账户是否拥有 MSMQ 的发送和接收权限。
  • Publisher 与 Subscriber 是否引用同一个消息契约程序集,避免两端使用名称相似但 CLR 类型不同的事件。
  • Publisher 的订阅存储配置是否可用。这里使用的是:
.MsmqSubscriptionStorage()

这项配置主要由 Publisher 使用,用于保存订阅信息。

当前配置中最直接的问题是 Subscriber 没有调用 .LoadMessageHandlers()。加载 handler 后,NServiceBus 才能发现订阅需求并完成自动订阅。

备注:内容仅供参考。