C# c中使用依赖注入的RabbitMQ工作队列#

C# c中使用依赖注入的RabbitMQ工作队列#,c#,.net-core,dependency-injection,rabbitmq,asp.net-core-2.0,C#,.net Core,Dependency Injection,Rabbitmq,Asp.net Core 2.0,我在“工作队列”场景中使用rabbitmq 例如,我需要一个由5个消费者组成的池(每个消费者都有自己的通道),这样一个进行I/O操作的消费者就不会阻塞同一队列中的其他消费者 例如。 如果我的队列中有: 消息1、消息2、消息3、消息4。(FistConsumerHandler)的每个实例将使用循环(默认rabbitmq行为)从队列中获取1条消息 我面临的问题是,我需要使用依赖项注入来实现这一点 以下是我到目前为止的情况: 在Windows服务启动时(我的使用者托管在Windows服务中): 消费者

我在“工作队列”场景中使用rabbitmq

例如,我需要一个由5个消费者组成的池(每个消费者都有自己的通道),这样一个进行I/O操作的消费者就不会阻塞同一队列中的其他消费者

例如。 如果我的队列中有: 消息1、消息2、消息3、消息4。(FistConsumerHandler)的每个实例将使用循环(默认rabbitmq行为)从队列中获取1条消息

我面临的问题是,我需要使用依赖项注入来实现这一点

以下是我到目前为止的情况:

在Windows服务启动时(我的使用者托管在Windows服务中):

消费者处理程序:

public class FistConsumerHandler : AsyncEventingBasicConsumer
{
    private readonly ILogger<FistConsumerHandler> _logger;

    private Guid guid = Guid.NewGuid();

    public FistConsumerHandler(
        IModel channel,
        ILogger<FistConsumerHandler> logger) : base(channel)
    {
        Received += ConsumeMessageAsync;
        _logger = logger;
    }

    private async Task ConsumeMessageAsync(object sender, BasicDeliverEventArgs eventArgs)
    {

        try
        {
            // consumer logic to consume the message
        }
        catch (Exception ex)
        {
            
        }
        finally
        {
            Model.Acknowledge(eventArgs);
        }
    }
}
公共类FistConsumerHandler:AsyncEventingBasicConsumer
{
专用只读ILogger\u记录器;
私有Guid=Guid.NewGuid();
公共财政顾问(
伊莫代尔海峡,
ILogger记录器):底座(通道)
{
已接收+=ConsumeMessageAsync;
_记录器=记录器;
}
专用异步任务ConsumeMessageAsync(对象发送方,BasicDeliverEventArgs eventArgs)
{
尝试
{
//使用消息的使用者逻辑
}
捕获(例外情况除外)
{
}
最后
{
模型确认(eventArgs);
}
}
}
此代码的问题是:

  • FistConsumerHandler只有1个实例(按singleton重新命名)。例如,我需要5个
  • 我只有一个通道,每个实例需要一个通道
  • 总之,使用Microsoft.Extensions.DependencyInjection的预期行为应该是:

  • 创建连接(与所有使用者共享此连接)
  • 当消息被接收到队列时,应该由1个使用者使用其自己的通道来使用它
  • 如果队列接收到另一条消息,则另一个使用者应使用该消息
  • TL;博士创建自己的范围 我在我正在开发的一个应用程序中也做过类似的事情,尽管没有我想要的那么干净(这也是我为什么看到这篇文章的原因)。对我来说,关键是使用IServiceScopeFactory获得注入式服务,并在消费者方法中使用它们。在一个典型的HTTP请求中,当请求传入/响应传出时,API将自动为您创建/关闭作用域。但由于这不是HTTP请求,我们需要创建/关闭使用注入服务的范围

    假设我已经设置了RabbitMQ使用者,将消息反序列化为对象(本例中为
    FooEntity
    ),这是一个获取注入DB上下文(但可以是任何内容)的简化示例:

    公共类RabbitMQConsumer
    {
    专用只读服务器ViceProvider(提供程序);
    公共RabbitMQConsumer(IServiceProvider服务提供者)
    {
    此。_serviceProvider=服务提供商;
    }
    公共异步任务ConsumeMessageAsync()
    {
    //使用语句可以确保在完成时关闭作用域,有助于避免内存泄漏
    使用(var scope=this.\u serviceProvider.CreateScope())
    {
    //在范围内获得您的服务
    var context=scope.ServiceProvider.GetRequiredService();
    //使用dbContext做事
    }
    }
    }
    
    确保在
    Startup.cs
    中将
    RabbitMQConsumer
    注册为单例,而不是瞬态

    参考资料:
    TL;博士创建自己的范围 我在我正在开发的一个应用程序中也做过类似的事情,尽管没有我想要的那么干净(这也是我为什么看到这篇文章的原因)。对我来说,关键是使用IServiceScopeFactory获得注入式服务,并在消费者方法中使用它们。在一个典型的HTTP请求中,当请求传入/响应传出时,API将自动为您创建/关闭作用域。但由于这不是HTTP请求,我们需要创建/关闭使用注入服务的范围

    假设我已经设置了RabbitMQ使用者,将消息反序列化为对象(本例中为
    FooEntity
    ),这是一个获取注入DB上下文(但可以是任何内容)的简化示例:

    公共类RabbitMQConsumer
    {
    专用只读服务器ViceProvider(提供程序);
    公共RabbitMQConsumer(IServiceProvider服务提供者)
    {
    此。_serviceProvider=服务提供商;
    }
    公共异步任务ConsumeMessageAsync()
    {
    //使用语句可以确保在完成时关闭作用域,有助于避免内存泄漏
    使用(var scope=this.\u serviceProvider.CreateScope())
    {
    //在范围内获得您的服务
    var context=scope.ServiceProvider.GetRequiredService();
    //使用dbContext做事
    }
    }
    }
    
    确保在
    Startup.cs
    中将
    RabbitMQConsumer
    注册为单例,而不是瞬态

    参考资料:
    using Microsoft.Extensions.DependencyInjection;
    using RabbitMQ.Client;
    using RabbitMQ.Client.Events;
    using System;
    using System.Collections.Generic;
    
    public class StartupBuilder
    {
        private static IConnection _connection;
    
        private IModel _channel;
    
        public List<ConsumerHandlerItem> _consumerHandlerItems;
    
        public IServiceCollection Services { get; private set; }
    
        public StartupBuilder(IConnection connection)
        {
            _connection = connection;
            _consumerHandlerItems = new List<ConsumerHandlerItem>();
            Services = new ServiceCollection();
        }
    
        public IServiceProvider Build()
        {
            _channel = _connection.CreateModel();
    
            Services.InitSerilog();
    
            // Add channel as singleton (this is not correct as I need 1 channel per ConsumerHandler)
            Services.AddSingleton(_channel);
    
            // Register the ConsumerHandler to DI 
            foreach (var item in _consumerHandlerItems)
            {
                // Add FirstHandler to DI
                Type consumerType = item.ConsumerType;
                Services.AddSingleton(consumerType);
            }
    
            // Finish DI Setup
            var serviceProvider = Services.BuildServiceProvider();
    
            // Bind the consumer handler to the channel and queue
            foreach (var item in _consumerHandlerItems)
            {
                var consumerHandler = (AsyncEventingBasicConsumer)serviceProvider.GetRequiredService(item.ConsumerType);
                _channel.AssignNewProcessor(item, consumerHandler);
            }
    
            return serviceProvider;
        }
    }
    
    public static class QueuesHelpers
    {
        public static void AssignNewProcessor(this IModel channel, ConsumerHandlerItem item, AsyncEventingBasicConsumer consumerHandler)
        {
            channel.ExchangeDeclare(item.Exchange, ExchangeType.Topic, durable: true);
            channel.QueueDeclare(item.Queue, true, false, false, null);
            channel.QueueBind(item.Queue, item.Exchange, item.Queue, null);
            channel.BasicConsume(item.Queue, false, consumerHandler);
        }
    }
    
    public class FistConsumerHandler : AsyncEventingBasicConsumer
    {
        private readonly ILogger<FistConsumerHandler> _logger;
    
        private Guid guid = Guid.NewGuid();
    
        public FistConsumerHandler(
            IModel channel,
            ILogger<FistConsumerHandler> logger) : base(channel)
        {
            Received += ConsumeMessageAsync;
            _logger = logger;
        }
    
        private async Task ConsumeMessageAsync(object sender, BasicDeliverEventArgs eventArgs)
        {
    
            try
            {
                // consumer logic to consume the message
            }
            catch (Exception ex)
            {
                
            }
            finally
            {
                Model.Acknowledge(eventArgs);
            }
        }
    }