11.生产者确认

在上一节中,即使对交换器、队列以及消息进行了持久化,但是不将消息进行配置,当消费者从队列中获得消息以后,消费者出现异常,并没有消费消息,那么消息在队列中已经被删除。对于这种情况,RabbitMQ提供了两种解决方案:事务机制和发送方确认机制

1.事务机制

可以通过channel.txSelect将当前信道设置为事务模式
通过channel.txcommint提交事务
通过channel.txRollBack进行事务的回滚

实例代码如下:

        channel.txSelect();
        for(int i=0;i<100;i++){
            try{
                channel.basicPublish("exchange","routingKey",null,message.getBytes());
                channel.txCommit();
            }catch (Exception e){
                e.printStackTrace();
                channel.txRollback();
            }
        }

正常情况下(没有出现异常,没有进行事务回滚)流程如下:


11.生产者确认_第1张图片
image.png

如果出现异常,需要进行事务回滚时,流程如下:


11.生产者确认_第2张图片
image.png

上述代码和流程是生产者和Broker之间的事务处理。事务确实能够解决消息发送方和RabbitMQ之间的消息确认问题,但是使用事务机制会严重影响RabbitMQ的性能,所以不推荐使用。

2.发送方确认机制

生产者将channel设置为confirm模式,所有从该信道上发布的消息都会被指派一个唯一的ID(从1开始),一旦消息被投递到所有匹配的队列之后,RabbitMQ就会发送一个basicAck给生产者(其中包含消息被指派的ID),这就使生产者知道消息已经正确送到目的地了。并且如果消息和队列是持久话的,那么basicAck会在消息写入磁盘之后发出。RabbitMQ回传给生产者的确认消息中,deliverTag包含了要确认消息的序号,此外RabbitMQ也可以设置multiple参数,表示在这之前的消息都已经得到了处理。


与事务机制的对比:
事务机制在一条消息发送之后会使得发送端阻塞,等待RabbitMq的回应之后才能发送下一条消息,而发送方确认机制却通过对消息指派唯一ID将其变成了异步执行。如果RabbitMQ因为自身的错误而导致消息丢失,就会发送一条basicNack命令,生产者可以在回调方法中处理该命令。

        channel.confirmSelect();
        for(int i=0;i<100;i++){
                channel.basicPublish("exchange","routingKey",null,message.getBytes());

                 if(!channel.waitForConfirms()){
                    //发送失败
                      dosomething...
                  }
          }  
          

总结:
事务机制和生产者确认机制是互斥的不能共存,从以上分析可以明显看出,使用生产者确认机制能够更加高效。
事务机制和生产者机制都能够确保消息正确发送到RabbitMQ, 但是如果这个交换器没有匹配的队列,那么消息也会丢失。这就需要将其和mandatory和备份交换器一起使用。
将mandatory参数设置为true,那么当交换器无法根据路由键找到一个合适队列的时候,会通过basicReturn返回给生产者;如果设置为false,则会将此消息丢弃。
使用备份交换器的话可以在生产者代码中不对basicReturn进行监听,无法根据路由键找到合适队列的消息会转发到备份交换器中。

3.批量confirm和异步confirm方法

批量confirm:每发送一批消息后,调用channel.waitForConfirm方法,等待服务器确认返回。
异步confirm:提供一个回调方法, 服务端确认了一条或者多条消息后,客户端会回调这个方法进行处理。

3.1.批量confirm

在批量confirm中,客户端程序需要定期或者定量或者定期定量来调用channel.wairForConfrim来等待RabbitMQ的确认返回,相比普通的confirm方法,极大提高了confirm的效率,但是如果出现返回basicNack或者超时情况,客户端需要将这一批次的消息全部重发,这会带来明显的重复消息,并且当消息经常丢失的时候,批量confirm的效率会有所降低。

3.2.异步confirm

在客户端Channel接口中提供的addConfirmListener方法中添加ConrimListener这个回调接口,这个回调接口包含两个方法,handleACK和handleNack,分别用来处理回传的basicAck和basicNack。两个方法都有一个参数deliveryTag(标志消息的唯一序号),我们可以利用SortedSet结构维护一个unconfirm消息序号集合,每发送一条消息,集合元素+1.每当调用handleAck方法时,unconfirm集合中删掉响应的一条或者多条记录(当multiple为false,删1条,为true,删多条)

你可能感兴趣的:(11.生产者确认)