简体   繁体   English

通过定时后台任务在 dotnet 核心中设置 Rabbitmq 消费者

[英]Setup Rabbitmq consumer in dotnet core by timed background task

I try to setup the rabbitmq consumer by timed background task , however, it's not always consuming message correctly.我尝试通过定时后台任务设置 rabbitmq 消费者,但是,它并不总是正确地使用消息。 It seems to get message from queue, but does not log the received message correctly.它似乎从队列中获取消息,但没有正确记录收到的消息。 This seems to be very weird.这似乎很奇怪。 I guess it's somehow related to the event handler receiving message not executed properly, but I just cannot figure out why.我想这与接收消息的事件处理程序未正确执行有关,但我无法弄清楚原因。

I do the timed background task as discussed in Background tasks with hosted services in ASP.NET Core because I want to avoid having a long lived connection.我执行定时后台任务,如ASP.NET 核心中的托管服务后台任务中所述,因为我想避免长期连接。 I feel it's more safe to create new connections to queue when needed.我觉得在需要时创建新连接到队列更安全。

However, when using background service with long lived connection, everything actually works fine so far.但是,当使用具有长期连接的后台服务时,到目前为止一切正常。

Can someone tell me what's wrong about the following code?有人可以告诉我以下代码有什么问题吗?

// consumer part
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;

namespace play_mq
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureLogging(logging =>
                {
                    logging.AddConsole();
                })
                .ConfigureServices(services =>
                {
                    services.AddHostedService<MessageListener>();
                });
    }

    public class MessageListener : IHostedService, IDisposable
    {
        private readonly IOptions<RabbitOptions> _rabbitOptions;
        private readonly ILogger<MessageListener> _logger;
        private Timer _timer;
        private int executionCount = 0;

        public MessageListener(ILogger<MessageListener> logger, IOptions<RabbitOptions> rabbitOptions)
        {
            _logger = logger;
            _rabbitOptions = rabbitOptions;
        }

        public Task StartAsync(CancellationToken cancellationToken)
        {
            _timer = new Timer(DoWork, null, TimeSpan.Zero,
                TimeSpan.FromSeconds(5));

            return Task.CompletedTask;

        }

        private void DoWork(object state)
        {
            var count = Interlocked.Increment(ref executionCount);

            _logger.LogInformation(
                "MessageListener is working. Count: {Count}", count);

            var factory = new ConnectionFactory()
            {
                HostName = "localhost"
            };

            using (var connection = factory.CreateConnection())
            using (var channel = connection.CreateModel())
            {
                channel.QueueDeclare(queue: "hello", durable: false, exclusive: false, autoDelete: false, arguments: null);
                var consumer = new EventingBasicConsumer(channel);
                consumer.Received += (model, ea) =>
                {
                    var body = ea.Body;
                    var message = Encoding.UTF8.GetString(body.ToArray());
                    _logger.LogInformation($"consume {message}");
                };
                channel.BasicConsume(queue: "hello", autoAck: true, consumer: consumer);
            }
        }

        public Task StopAsync(CancellationToken cancellationToken)
        {
            _logger.LogInformation("MessageListener is stopping.");

            _timer?.Change(Timeout.Infinite, 0);

            return Task.CompletedTask;
        }

        public void Dispose()
        {
            _timer?.Dispose();
        }
    }

}
// Sender comes from official example
using System;
using RabbitMQ.Client;
using System.Text;

namespace ConsoleMq
{
    class Send
    {
        public static void Main()
        {
            var factory = new ConnectionFactory() { HostName = "localhost", Port = 14000 };
            using (var connection = factory.CreateConnection())
            using (var channel = connection.CreateModel())
            {
                channel.QueueDeclare(queue: "hello", durable: false, exclusive: false, autoDelete: false, arguments: null);

                string message = "Hello World!";
                var body = Encoding.UTF8.GetBytes(message);

                channel.BasicPublish(exchange: "", routingKey: "hello", basicProperties: null, body: body);
                Console.WriteLine(" [x] Sent {0}", message);
            }

            Console.WriteLine(" Press [enter] to exit.");
            Console.ReadLine();
        }
    }
}

It seems to get message from queue, but does not log the received message correctly.它似乎从队列中获取消息,但没有正确记录收到的消息。

Reason:原因:

channel.BasicConsume(queue: "hello", autoAck: true, consumer: consumer);

autoAck set to true, It ack positive to a broker before receiving a message. autoAck 设置为 true,它在接收消息之前向代理确认。 and broker remove it from the queue once it ack +并且经纪人在确认后将其从队列中删除 +

Solution : Instead of Setting ack(true) there(Set auroAck = false), And Ack when you receive message解决方案:而不是设置 ack(true) 那里(设置 auroAck = false),并在收到消息时确认

consumer.Received += (model, ea) =>
            {
               channel.BasicAck(deliveryTag: ea.DeliveryTag, multiple: false);
               //Other method available for negative ack
            };

I want to avoid having a long lived connection.我想避免建立长期的联系。 I feel it's more safe to create new?connections to queue when needed.我觉得在需要时创建新的连接到队列更安全。

For a better answer to your question, You can check MassTransit (RabbitMQ wrapper) code.为了更好地回答您的问题,您可以查看MassTransit (RabbitMQ 包装器)代码。

And to receive messages as it publishes在发布时接收消息

public class MessageListener : IHostedService, IDisposable
    {
        private readonly IOptions<RMQConfig> _rabbitOptions;
        private readonly ILogger<MessageListener> _logger;
        private Timer _timer;
        private int executionCount = 0;
        private readonly IConnection _connection;

        public MessageListener(ILogger<MessageListener> logger, IOptions<RMQConfig> rabbitOptions)
        {
            _logger = logger;
            _rabbitOptions = rabbitOptions;
            var factory = new ConnectionFactory()
            {
                HostName = "localhost"
            };
            _connection = factory.CreateConnection();
        }

        public Task StartAsync(CancellationToken cancellationToken)
        {
            _timer = new Timer(DoWork, null, TimeSpan.Zero,
                TimeSpan.FromSeconds(5));

            return Task.CompletedTask;

        }

        private void DoWork(object state)
        {
            var count = Interlocked.Increment(ref executionCount);

            _logger.LogInformation(
                "MessageListener is working. Count: {Count}", count);



            // using (var connection = factory.CreateConnection())
            // using (var channel = connection.CreateModel())
            // { 
            var channel = _connection.CreateModel();

            channel.QueueDeclare(queue: "hello", durable: false, exclusive: false, autoDelete: false, arguments: null);
            var consumer = new EventingBasicConsumer(channel);
            consumer.Received += (model, ea) =>
            {
                var body = ea.Body;
                var message = Encoding.UTF8.GetString(body.ToArray());
                _logger.LogInformation($"consume {message}");
            };
            channel.BasicConsume(queue: "hello", autoAck: false, consumer: consumer);
            // }
        }

        public Task StopAsync(CancellationToken cancellationToken)
        {
            _logger.LogInformation("MessageListener is stopping.");

            _timer?.Change(Timeout.Infinite, 0);

            return Task.CompletedTask;
        }

        public void Dispose()
        {
            //Close _connection
            _timer?.Dispose();
        }
    }

I faced the same issue a few months back, Hope It will help others.几个月前我遇到了同样的问题,希望它会帮助其他人。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM