zoukankan      html  css  js  c++  java
  • RabbitMQ (十二) 消息确认机制

    消费者确认解决的问题是确认消息是否被消费者"成功消费".

    它有个前提条件,那就是生产者发布的消息已经"成功"发送出去了.

    因此还需要一个机制来告诉生产者,你发送的消息真的"成功"发送了.

    在标准的AMQP 0-9-1,保证消息不会丢失的唯一方法是使用事务:在通道上开启事务,发布消息,提交事务.但是事务是非常重量级的,它使得RabbitMQ的吞吐量降低250倍.为了解决这个问题,RabbitMQ 引入了 发布者确认(Publisher Confirms) 机制,它是模仿AMQP协议中的消费者消息确认机制.

    事务机制

    生产者部分代码:

             try
                {               
                    channel.TxSelect();//开启事务机制               
                    channel.BasicPublish("", QueueName, null, Encoding.Default.GetBytes("hello world"));              
                    channel.TxCommit();//提交
                    Console.WriteLine($"send {msg}");
                }
                catch (Exception e)
                {                
                    channel.TxRollback();//回滚
                    Console.WriteLine(e);
                }

    发布者确认

    一旦在信道上使用 confirm.select 方法,就认为该信道处于Publisher Confirms模式.事务信道不能进入Publisher Confirms模式,一旦信道处于Publisher Confirms模式,不能开启事务.即事务和Publisher Confirms模式只能二选一.

    发布的消息什么时候会被broker确认?

    对于不可路由的消息,broker 将在 exchange 验证消息不会路由到任何队列(发回一个空的队列列表)后发出确认;如果消息被设置为"必需消息"发布,即 BasicPublish() 方法的 "mandatory" 入参为true,那么 BasicReturn 事件将在 BasicAcks 事件之前触发.否定确认 BasicNacks 事件也是如此.

    对于可路由消息,当所有队列都接受消息时才触发 BasicAcks 事件,对于路由到持久话队列的持久性消息,这意味着持久化到磁盘后才会触发 BasicAcks 事件;对于消息的镜像队列,这意味着所有镜像都已接受该消息后才会触发 BasicAcks 事件.

    发布者确认分为同步和异步两种.

    一.同步

    生产者部分代码

                //开启confirm机制
                channel.ConfirmSelect();
                string msg = "hello world ";           
                for (int i = 0; i < 10; i++)
                {
                    channel.BasicPublish("", QueueName, null, Encoding.Default.GetBytes(msg + i));
                }
              
            //可以发送一批消息后,调用该方法;也可以每发一条调用一次.       
                if (channel.WaitForConfirms())
                {
                    Console.WriteLine("send is success");
                }
                else
                {
                    Console.WriteLine("send is failed");
             //实际应用中,这里需要添加发送消息失败的处理逻辑.
             //如果是发送一批消息,那么只要有一条失败,则所有的消息发送都会失败. }

    二.异步

    生产者部分代码

              channel.ConfirmSelect();  

    //肯定确认 channel.BasicAcks += (s, e) => { //多条 if (e.Multiple) { Console.WriteLine("最后成功的一条是 : " + e.DeliveryTag); } //单条 else { Console.WriteLine(e.DeliveryTag + " 成功发送 "); } }; //否定确认 channel.BasicNacks += (s, e) => { //多条 if (e.Multiple) { Console.WriteLine("最后失败的一条是 : " + e.DeliveryTag); } //单条 else { Console.WriteLine(e.DeliveryTag + " 发送失败 "); } };

    发布者的否定确认(BasicNacks)

    • 在特殊情况下,当 broker 无法成功处理消息而不是 BasicAck 时,broker 将发送 BasicNack.在这种情况下,BasicNack 的字段与 BasicAck 相对应的字段意义相同,并且 requeue 字段是没有意义的.是否重发消息由发送者自己决定;
    • 将channel设置为发布者确认模式后,所有后续发布的消息都只会被 confirm 一次或者 nack 一次;
    • 没有机制保证消息需要多久被 confirmed;
    • 消息不会同时被confirmed和nack`d;
    • BasicNacks 事件只在负责队列的Erlang进程中发生内部错误时才会触发;

    持久化消息的延迟肯定确认

    前面说到,

    如果是持久化的消息,要等到消息持久化到磁盘后才会触发 BasicAcks 事件;对于消息的镜像队列,要等到所有镜像都已接受该消息后才会触发 BasicAcks 事件.

    而为了保证持久化效率, RabbitMQ不是来一条存一条,而是定时批量地持久化消息到磁盘.RabbitMQ 消息存储一段时间(几百毫秒)之后或者当队列空闲时,才会批量写到磁盘.

    这意味着在恒定负载下,BasicAck 的延迟可以达到几百毫秒.如果队列支持镜像队列,则延迟时间更大.

    所以,为了提高吞吐量,强烈建议应用程序采用异步确认方式,或者发布批量消息后等待确认.

    发布者确认的注意事项

    在大多数情况下,RabbitMQ将以与发布时相同的顺序向发布者确认消息(这适用于在单个频道上发布的消息).但是,发布者确认是异步发出的,可以确认单个消息或一组消息.发出确认的确切时刻取决于消息的传递模式(持久性与瞬态)以及消息路由到的队列的属性.也就是说,RabbitMQ可能不以消息发布的顺序向发布者发送确认消息.生产者端尽量不要依赖消息确认的顺序处理业务逻辑. 

  • 相关阅读:
    CodeDOM浅析(上)
    这段代码居然运行正常
    CodeDOM浅析(下)
    动态生成与编译(七)根据CodeDOM生成源代码
    动态生成与编译(九)CodeDOM的局限
    动态生成与编译(八)动态编译
    新年送大礼
    祝所有的女性朋友们节日快乐!
    预测人民币在2006年最终的收盘价
    2007年春节,祝朋友们:身体健康,万事如意! 度过一个愉快春节!
  • 原文地址:https://www.cnblogs.com/refuge/p/10357546.html
Copyright © 2011-2022 走看看