C# RabbitMQ使用者恢复

C# RabbitMQ使用者恢复,c#,windows-services,rabbitmq,C#,Windows Services,Rabbitmq,我最近开始潜入拉比犬。我使用RabbitMQ.net库创建了一个Windows服务,该库充当我的使用者。该消费者将用于处理批量处理,如发送大批量电子邮件等 我通过实现RabbitMQ.net库中的simplercserver类,并重写HandleCall/HandleCast方法来构建它。在消费和处理消息方面,一切都很好。我们已经开始研究可用于将此Windows服务部署到AmazonWeb服务服务器的部署选项。将更新部署到windows服务时,必须停止、更新该服务,然后重新启动 我的问题是:我该

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

我通过实现RabbitMQ.net库中的
simplercserver
类,并重写
HandleCall/HandleCast
方法来构建它。在消费和处理消息方面,一切都很好。我们已经开始研究可用于将此Windows服务部署到AmazonWeb服务服务器的部署选项。将更新部署到windows服务时,必须停止、更新该服务,然后重新启动

我的问题是:我该怎么做才能在Windows服务上触发
停止
事件时,该服务要么等待当前发送给消费者的所有消息完成处理,要么重新查询已发送但尚未开始处理的任何消息

以下是一些示例代码:

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
        }
    }
}
公共部分类ExampleService:ServiceBase
{    
私有列表_watcherTasks=新列表();
启动时受保护的覆盖无效(字符串[]args)
{
var factory=new ConnectionFactory(){
主机名=\u主机名,
虚拟主机=_虚拟主机,
用户名=\u用户名,
密码=\u密码,
Ssl=新的SslOption
{
启用=真,
服务器名=\u主机名,
AcceptablePolicyErrors=SslPolicyErrors.RemoteCertificateNameMatch|
SslPolicyErrors.RemoteCertificateChaineErrors
},
RequestedHeartbeat=30
};
conn=factory.CreateConnection();
var emailQueue=requestChannel.QueueDeclare(“电子邮件”,false,false,false,null);
var emailSub=新订阅(requestChannel、emailQueue);
var emailServer=新的ServiceBusConsumer(emailSub);
任务emailWatcher=Task.Run(()=>emailServer.MainLoop());
_添加(emailWatcher);
}
受保护的覆盖void OnStop()
{
康涅狄格州关闭();
Task.WaitAll(_watcherTasks.ToArray(),60000);
}
}
公共类ServiceBusConsumer:SimpleRpcServer
{
public ServiceBusConsumer(订阅):基本(订阅)
{
}
public override void HandleSimpleCast(bool isRedelivered,RabbitMQ.Client.IBasicProperties requestProperties,byte[]body)
{
尝试
{
//在这里使用一些反射并调用函数来处理消息。
}
捕获(例外情况除外)
{
//创建异常的事件日志条目
}
}
}

在消息被使用后,不能显式地将消息推回到队列中。如果没有发送确认,并且队列配置为接收确认,则终止已使用消息的进程应导致消息在超时后重新排队。考虑一下,这可能会打断队列中消息的排序。

例如,假设您发送消息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#library,它可以完全满足您的要求。请查看我的博客,了解详细信息和教程。您可能还对以下内容感兴趣。

消息也可以被拒绝并重新查询,而无需停止使用者。它们可以,但如果有发布者持续将消息推送到队列,则使用者将持续忙于拒绝消息,从而延迟部署过程。这里的要点是及时优雅地停止服务,以便可以在AWS上应用更新。如果要停止从队列接收消息,请使用basic.cancel命令。确定。这将要求您的消费者进入一个错误状态,而不是尽可能优雅地退出。这里有很多建议来实现所要求的。这与我最终所做的非常相似。我选择重写ProcessRequest函数来检查服务是否正在关闭。如果是这样,它将停止使用消息并优雅地关闭连接。工作得很好。谢谢大家的帮助!