如何停止 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 文档中读到 运行 公交车可能会阻止正常退出。
- 这是 Kestrel Web 服务器的主要问题吗?我读过有关进程回收的内容,但恐怕 运行 总线会影响这一点。
- 我可以将
bus.Stop()
放在 ASP.NET 核心项目中的哪个位置?
您可以使用 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();
}
即使在进程关闭时显式释放资源总是好的。例如,当进程在关闭后将被杀死时,某些消息可能仍在转换中。进行显式处置将允许完成此转换。
要添加到现有答案中:
- 如果您使用
MassTransit.AspNetCore
的 IServiceCollection.AddMassTransit()
扩展方法,则不需要 class 级别 IBus
实例。 Startup
的 Configure()
支持 DI,所以你可以这样做:
public void Configure(IApplicationLifetime appLifetime, IBus bus)
{
appLifetime.ApplicationStarted.Register(() => bus.Start());
appLifetime.ApplicationStopping.Register(() => bus.Stop());
}
如果您不想使用该包,您仍然可以向 DI 容器注册 IBus(如问题中所示)并从 Configure()
. 请求它
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。我不确定我是否喜欢使用后台服务来停止公共汽车的想法。不管怎样,我从这个例子中挑选出来的样本库值得参考。
第三种选择是在Main()
自己动手。类似于:
var host = CreateWebHostBuilder(args).Build();
var bus = host.Services.GetRequiredService<IBusControl>();
await bus.StartAsync();
await host.RunAsync();
await bus.StopAsync();
我正在研究 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 文档中读到 运行 公交车可能会阻止正常退出。
- 这是 Kestrel Web 服务器的主要问题吗?我读过有关进程回收的内容,但恐怕 运行 总线会影响这一点。
- 我可以将
bus.Stop()
放在 ASP.NET 核心项目中的哪个位置?
您可以使用 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();
}
即使在进程关闭时显式释放资源总是好的。例如,当进程在关闭后将被杀死时,某些消息可能仍在转换中。进行显式处置将允许完成此转换。
要添加到现有答案中:
- 如果您使用
MassTransit.AspNetCore
的IServiceCollection.AddMassTransit()
扩展方法,则不需要 class 级别IBus
实例。Startup
的Configure()
支持 DI,所以你可以这样做:
如果您不想使用该包,您仍然可以向 DI 容器注册 IBus(如问题中所示)并从public void Configure(IApplicationLifetime appLifetime, IBus bus) { appLifetime.ApplicationStarted.Register(() => bus.Start()); appLifetime.ApplicationStopping.Register(() => bus.Stop()); }
Configure()
. 请求它
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。我不确定我是否喜欢使用后台服务来停止公共汽车的想法。不管怎样,我从这个例子中挑选出来的样本库值得参考。第三种选择是在
Main()
自己动手。类似于:var host = CreateWebHostBuilder(args).Build(); var bus = host.Services.GetRequiredService<IBusControl>(); await bus.StartAsync(); await host.RunAsync(); await bus.StopAsync();