RabbitMQ消费者恢复

时间:2014-11-19 22:59:48

标签: c# windows-services rabbitmq

我最近开始潜入RabbitMQ。我使用RabbitMQ .net库创建了一个Windows服务,该库充当我的消费者。此消费者将用于处理批量处理,例如发送大批电子邮件等。

我通过实现作为RabbitMQ .net库一部分的SimpleRpcServer类并覆盖HandleCall/HandleCast方法来构建它。在消费和处理消息方面,一切都很有效。我们已开始研究可用于将此Windows服务部署到Amazon Web Services服务器的部署选项。将更新部署到Windows服务时,必须停止,更新服务,然后再次启动。

我的问题是:我可以做什么,以便在Windows服务上触发Stop事件时,该服务会等待所有当前已发送的邮件给消费者以完成处理并重新排队已发送但尚未开始处理的任何消息。

以下是一些示例代码:

public partial class ExampleService: ServiceBase
{    
    private List<Task> _watcherTasks = new List<Task>();
    protected override void OnStart(string[] args)
    {
        var factory = new ConnectionFactory() { 
                HostName = _hostname,
                VirtualHost = _virtualHost,
                UserName = _username,
                Password = _password,
                Ssl = new SslOption
                {
                    Enabled = true,
                    ServerName = _hostname,
                    AcceptablePolicyErrors = SslPolicyErrors.RemoteCertificateNameMismatch |
                                            SslPolicyErrors.RemoteCertificateChainErrors
                },
                RequestedHeartbeat = 30
            };
            conn = factory.CreateConnection();

            var emailQueue = requestChannel.QueueDeclare("email", false, false, false, null);
            var emailSub = new Subscription(requestChannel, emailQueue);
            var emailServer = new ServiceBusConsumer(emailSub);
            Task emailWatcher = Task.Run(() => emailServer.MainLoop());            
            _watcherTasks.Add(emailWatcher);
     }

     protected override void OnStop()
     {
          conn.Close();
          Task.WaitAll(_watcherTasks.ToArray(), 60000);    
     }
}

public class ServiceBusConsumer : SimpleRpcServer
{
    public ServiceBusConsumer(Subscription subscription) : base(subscription)
    {

    }
    public override void HandleSimpleCast(bool isRedelivered, RabbitMQ.Client.IBasicProperties requestProperties, byte[] body)
    {
        try
        {
             //Uses some reflection and invokes function to process the message here.
        }
        catch (Exception ex)
        {
             //Creates event log entry of exception
        }
    }
}

1 个答案:

答案 0 :(得分:2)

消费后,您无法明确地将消息推送回队列。如果没有发送确认,并且您的队列配置为接收确认,则终止已消耗该消息的进程应使该消息在超时期限后重新排队。但请注意,这可能会中断队列中消息的排序。

例如,假设你发送消息M1和M2,其中M2依赖于M1。如果您在处理M1时终止您的消费者,并且当M2仍然在队列中时,M1将在M2后面重新排队。当您的消费者重新启动时,这些消息将被无序处理。

尝试实现这一目标会带来太多复杂性。我只是允许你的Windows服务完成处理它的排队消息,然后只是停止监听队列,并优雅地终止。你的代码应该是这样的:

while (!stopConsuming) {
    try {
        BasicDeliverEventArgs basicDeliverEventArgs;
        var messageIsAvailable = consumer.Queue.Dequeue(timeout, out basicDeliverEventArgs);

        if (!messageIsAvailable) continue;
            var payload = basicDeliverEventArgs.Body;

            var message = Encoding.UTF8.GetString(payload);
            OnMessageReceived(new MessageReceivedEventArgs {
                Message = message,
                EventArgs = basicDeliverEventArgs
            });

            if (implicitAck && !noAck) channel.BasicAck(basicDeliverEventArgs.DeliveryTag, false);
        }

        catch (Exception exception) {
            OnMessageReceived(new MessageReceivedEventArgs {
                Exception = new AMQPConsumerProcessingException(exception)
            });
            if (!catchAllExceptions) Stop();
        }
    }
}

在此示例中,stopConsuming变量(如果从另一个线程访问,则标记为volatile)将确定Windows服务在完成处理后是否将继续从队列中读取消息当前的消息。如果设置为true,则循环将结束,不再有消息将被排队。

我即将部署一个C#库,它可以完全满足您的需求。请查看我的博客insidethecpu.com以获取详细信息和教程。您可能也对this感兴趣。