.NET通用主机 - 是否可以停止和重新启动主机?

3
考虑这个非常简单的.NET Core 3.1(和.NET 5)应用程序,没有特殊的配置或托管服务:
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;

internal class Program
{
    public static async Task Main(string[] args)
    {
        var builder = Host.CreateDefaultBuilder(args);
        builder.UseWindowsService();
        var host = builder.Build();

        var fireAndforget = Task.Run(async () => await host.RunAsync());
        await Task.Delay(5000);
        await host.StopAsync();
        await Task.Delay(5000);
        await host.RunAsync();
    }

第一次运行(仅作为后台“fire and forget”任务发送,仅用于此测试目的)和停止成功。在第二次调用Run时,我收到了以下异常:
System.AggregateException:“对象名称:'EventLogInternal'。无法访问已处理的对象。对象名称:'EventLogInternal'。”
如果我使用StartAsync而不是RunAsync执行相同的操作(这次不需要fireAndForget),则在第二次调用StartAsync时会收到System.OperationCanceledException。
我是否可以推断出.NET通用主机不适合停止和重新启动?

我为什么需要这个?

我的目标是有一个单独的应用程序作为Windows服务运行,该应用程序将托管两个不同的.NET通用主机。这是基于here的建议,以便具有单独的配置和依赖注入规则以及消息队列。
其中一个服务会在应用程序的整个生命周期内保持活动状态(直到在Windows服务中停止该服务),并作为接收消息事件的入口点,启动/停止另一个服务,后者将成为具有完整服务的主处理主机。这样,主要服务可以处于“空闲”状态,直到它们接收到触发其进程的消息,另一个消息可以将其返回到空闲状态。
2个回答

1
CreateDefaultBuilder(...).Build()返回的主机表示整个应用程序。来自docs的引用如下:

将所有应用程序的相互依赖资源包含在一个对象中的主要原因是生命周期管理:控制应用程序的启动和优雅关闭。

默认构建器在单例范围内注册许多服务,当主机停止时,所有这些服务都会被处理或切换到某种“已停止”状态。例如,在调用StopAsync之前,您可以解析IHostApplicationLifetime
var appLifetime = host.Services.GetService<IHostApplicationLifetime>();

它具有表示应用程序状态的取消标记。当您在停止后调用StartAsync或RunAsync时,所有标记仍将IsCancellationRequested设置为true。这就是为什么在Host.StartAsync中抛出OperactionCancelledException的原因。

您可以在配置期间列出其他服务: enter image description here

对我来说,听起来您只需要一些后台作业来处理消息,但我从未使用过NServiceBus,因此不知道如何与类似Hangfire的东西配合使用。您还可以实现IHostedService并在通用主机构建器中使用它。


谢谢。所以答案实际上是“不,IHost并不意味着在应用程序的生命周期内停止和重新启动”。我的问题来自于我使用的库的用法,它不是通过在ConfigureService中提供服务来完成的,而是通过对IHostBuilder的扩展方法来完成的。我会与他们跟进。 - Dunge
是的,简短的回答是“不”。我只是对此不太确定,所以查看了源代码。从技术上讲,你可以每次需要时构建主机,但这听起来有点过度。 - mcbr
1
FYI:即使每次构建新的主机,在第二次停止时也会出现“EventLogInternal”异常导致崩溃。当使用Ctrl+C触发应用程序关闭时也是如此。他们似乎真的不允许在同一个应用程序中使用多个IHost。 - Dunge

0
我正在做类似这样的事情:
do
{
    using IHost host = BuildHost();
    await host.RunAsync();
} while (MainService.Restart);

在MainService构造函数中:

public MainService(IHostApplicationLifetime HostApplicationLifetime)

MainService.Restart 是一个静态布尔值,由 MainService 自身响应某些事件设置,该事件还调用了 HostApplicationLifetime.StopApplication()


1
如果您的应用程序使用UseWindowsService在Windows服务中托管,则会从WindowsServiceLifetime获取“System.InvalidOperationException:Stopped without starting”异常。它是基于_delayStart的私有Run方法中抛出的,尽管我无法弄清楚如何避免这种情况。https://github.com/dotnet/runtime/blob/main/src/libraries/Microsoft.Extensions.Hosting.WindowsServices/src/WindowsServiceLifetime.cs - tjmoore
1
你说得对,我最近也发现了这一点。 我们暂时设置了主机Windows服务在崩溃后自动重启,但显然这不是一个真正的解决方案。 - Jck

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接