在 .NET Core 中正确初始化后台服务的方法

5

我有一个服务需要在启动时连接到另一个服务。

另一个服务是 Rabbitmq 代理。

我正在监听来自 Rabbitmq 的某些事件,所以我需要从应用程序开始时就激活它。

我需要连接到两个不同的 VHost,因此需要创建两个连接。

问题是当我启动应用程序时,它会不断地创建连接,直到服务器崩溃!

在 Rabbitmq 管理中,可以看到创建了大量的 ConnectionChannels

我找不到原因。

通常,我想知道在 .NET Core 应用程序启动时连接到其他服务的正确方法是什么。

我使用以下代码来实现:

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

            services.AddSingleton<RabbitConnectionService>();

            ...

            ActivatorUtilities.CreateInstance<RabbitConnectionService>(services.BuildServiceProvider());
        }

RabbitConnectionService的构造函数中,我正在连接RabbitMQ
public RabbitConnectionService(IConfiguration configuration)
        {   
            ServersMessageQueue = new MessageQueue(configuration.GetConnectionString("FirstVhost"), "First");
            ClientsMessageQueue = new MessageQueue(configuration.GetConnectionString("SecondVhost"), "Second");
        }

消息队列类:

public class MessageQueue
    {

        private IConnection connection;

        private string RabbitURI;
        private string ConnectionName;

        static Logger _logger = LogManager.GetCurrentClassLogger();

        public MessageQueue(string connectionUri, string connectionName)
        {
            ConnectionName = connectionName;
            RabbitURI = connectionUri;
            connection = CreateConnection();
        }


        private IConnection CreateConnection()
        {
            ConnectionFactory factory = new ConnectionFactory();
            factory.Uri = new Uri(RabbitURI);
            factory.AutomaticRecoveryEnabled = true;
            factory.RequestedHeartbeat = 10;
            return factory.CreateConnection(ConnectionName);
        }

        public IModel CreateChannel()
        {
            return connection.CreateModel();
        }

        ...
    }

你在这里真的没有提供足够的信息。例如,为什么在你的“ConfigureServices”中调用ActivatorUtilities.CreateInstance,然后不对结果进行任何操作? - Ian Kemp
我正在从Rabbitmq监听某些事件,因此我需要它在应用程序启动时被激活。如果我不把它放在那里,它将不会在第一个请求到达我的服务器之前启动。 - AminSojoudi
3
啊,我现在明白了。在.NET Core中,ConfigureServices方法的命名不够合适,它应该被称为ConfigureDependencies或类似的名称。无论如何,你正在寻找的是后台任务,MSDN有相关文档:https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-2.2&tabs=visual-studio#consuming-a-scoped-service-in-a-background-task-1 - Ian Kemp
也许我的回答可以帮到你 https://stackoverflow.com/questions/58535975/how-can-i-order-asp-net-core-runs-a-method-once-per-day/58537020#58537020 - Tony Ngo
@IanKemp 谢谢,你回答了我的问题。 - AminSojoudi
1个回答

4
为了启用后台处理,您需要创建一个实现IHostedService接口的类。
public interface IHostedService
{
    Task StartAsync(CancellationToken cancellationToken);
    Task StopAsync(CancellationToken cancellationToken);
}

这个接口有两个方法StartAsyncStopAsync。你需要使用依赖注入来注册服务,像这样:
public void ConfigureServices(IServiceCollection services)
{
    ...
    services.AddSingleton<IHostedService, DemoService>();
    ...
}

不必实现IHostedService接口,您可以从BackgroundService抽象类派生,并实现ExecuteAsync抽象方法。以下是一个最小化的后台服务示例,它监视SQL Server中的表格并发送电子邮件:

public class DemoService : BackgroundService
{
    private readonly ILogger<DemoService> _demoservicelogger;
    private readonly DemoContext _demoContext;
    private readonly IEmailService _emailService;
    public DemoService(ILogger<DemoService> demoservicelogger, 
        DemoContext demoContext, IEmailService emailService)
    {
        _demoservicelogger = demoservicelogger;
        _demoContext = demoContext;
        _emailService = emailService;
    }
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        _demoservicelogger.LogDebug("Demo Service is starting");
        stoppingToken.Register(() => _demoservicelogger.LogDebug("Demo Service is stopping."));
        while (!stoppingToken.IsCancellationRequested)
        {
            _demoservicelogger.LogDebug("Demo Service is running in background");
            var pendingEmailTasks = _demoContext.EmailTasks
                .Where(x => !x.IsEmailSent).AsEnumerable();
            await SendEmailsAsync(pendingEmailTasks);
            await Task.Delay(1000 * 60 * 5, stoppingToken);
        }
        _demoservicelogger.LogDebug("Demo service is stopping");
    }
}

感谢Ian Kemp。

参考资料


1
很棒的答案。如果服务正在侦听一个端点,并在电子邮件准备好发送时触发事件,那该怎么办?您不需要 while 循环或延迟,但我不确定 ExecuteAsync 方法中的异步逻辑将如何处理。 - Murphybro2
1
@Murphybro2 我相信你现在已经解决了这个问题,但是对于其他人来说,如果你只有一个事件监听器,那么你不需要使用 BackgroundService,而是使用 @AminSojoudi 的第一个建议,简单地实现 IHostedService,然后在启动时连接你的事件处理程序,在停止时取消连接。 - Tim Burris

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