如何停止 ASP.NET 核心服务器中的 MassTransit 巴士?

How to stop a MassTransit bus in an ASP.NET Core server?

我正在研究 MassTransit 和 ASP.NET 核心、依赖注入并成功实现了一些有用的东西。我打算使用 Kestrel Web 服务器。

所以我不得不在 Startup.cs 中以这种方式配置我的 ASP.NET 核心项目。

public void ConfigureServices(IServiceCollection services) {

    ...

    var bus = Bus.Factory.CreateUsingRabbitMq(sbc => {
        var host = sbc.Host(new Uri(address), h => {
            h.Username("guest");
            h.Password("guest");
        });
    });

    services.AddSingleton<IBus>(bus);
    services.AddScoped<IRequestClient<GetTagRequest, GetTagAnswer>>(x =>
        new MessageRequestClient<GetTagRequest, GetTagAnswer>(x.GetRequiredService<IBus>(), new Uri(address + "/gettagrequest"), timeOut));

    bus.Start(); // <= ok. how is the bus supposed to stop ?

    ...

虽然这工作正常,但没有教程提到任何关于在 ASP.NET 核心项目中设置 bus.Stop() 的内容。我在 MassTransit 文档中读到 运行 公交车可能会阻止正常退出。

您可以使用 ApplicationLifetime 事件。只需将您的 IBus 对象设为 class 级别变量即可。

public class Startup
{
    private IBus _bus;

    public void ConfigureServices(IServiceCollection services) {
        /* ... */

        _bus = Bus.Factory.CreateUsingRabbitMq ... 

        /* ... */
    }

    public void Configure(IApplicationLifetime appLifetime)
    {
        appLifetime.ApplicationStarted.Register(() => _bus.Start());
        appLifetime.ApplicationStopping.Register(() => _bus.Stop());
    }
}

.NET Core 中有 IApplicationLifetime,它有几个 CancellationToken 属性,包括 ApplicationStopped。因此,当您需要在 asp.net 应用程序关闭并且所有请求都已处理(例如停止您的公共汽车)之后做某事时 - 您可以这样做:

// lifetime will be injected to Configure from DI container
public void Configure(IApplicationBuilder app, IApplicationLifetime lifetime) {
    // subscribe to ApplicationStopped
    lifetime.ApplicationStopped.Register(OnApplicationStopped);
    // the rest
}

private void OnApplicationStopped() {
    _bus.Stop();
}

即使在进程关闭时显式释放资源总是好的。例如,当进程在关闭后将被杀死时,某些消息可能仍在转换中。进行显式处置将允许完成此转换。

要添加到现有答案中:

  1. 如果您使用 MassTransit.AspNetCoreIServiceCollection.AddMassTransit() 扩展方法,则不需要 class 级别 IBus 实例。 StartupConfigure() 支持 DI,所以你可以这样做:
    public void Configure(IApplicationLifetime appLifetime, IBus bus)
    {
        appLifetime.ApplicationStarted.Register(() => bus.Start());
        appLifetime.ApplicationStopping.Register(() => bus.Stop());
    }
    
    如果您不想使用该包,您仍然可以向 DI 容器注册 IBus(如问题中所示)并从 Configure().
  2. 请求它
  3. ASP.NET Core DI sample 使用 IHostedService 代替:

    public class BusService : IHostedService
    {
        private readonly IBusControl _busControl;
        public BusService(IBusControl busControl)
        {
            _busControl = busControl;
        }
    
        public Task StartAsync(CancellationToken cancellationToken) =>
            _busControl.StartAsync(cancellationToken);
    
        public Task StopAsync(CancellationToken cancellationToken) =>
            _busControl.StopAsync(cancellationToken);
    }
    

    服务注册为:

    services.AddSingleton<IHostedService, BusService>();
    

    有关 IHostedService 的更多信息,请点击此处 doc page。我不确定我是否喜欢使用后台服务来停止公共汽车的想法。不管怎样,我从这个例子中挑选出来的样本库值得参考。

  4. 第三种选择是在Main()自己动手。类似于:

    var host = CreateWebHostBuilder(args).Build();
    var bus = host.Services.GetRequiredService<IBusControl>();
    
    await bus.StartAsync();
    await host.RunAsync();
    await bus.StopAsync();