SpringBoot used RabbitMQ (ii) confirmation message

Message acknowledgment.

  • Sending an acknowledgment from the manufacturer to know whether the message was successfully sent to the queue or switch
  • After confirming the consumer end consumer successful consumer news, send a confirmation to identify the message is removed from the MQ

How to send a message to determine the success or failure?

  • When an acknowledgment message is not routed to any queue, it recognizes transmission failure
  • When the message can be routed to a queue, the queue when you want to send are sent successfully, a message confirming successful. For persistent queue means has been written to disk for mirroring the queue, which means that all mirrors to accept success.

How to inform the consumer end consumer news rabbitmq success or failure?

  • Automatic confirmation will be confirmed immediately after the message is sent to the consumer, but there may be lost messages, if the consumer end consumer logic throws an exception, that is, the consumer does not end successfully processed the message, then the equivalent of lost messages
  • If the manual confirmation when consumers call ack, nack, reject confirmation several methods, some operations may be performed manually confirm operations after a failure, if the message was not an ACK is sent to the next consumer
  • If a service forget the ACK, the RabbitMQ will not send data to it, because RabbitMQ considered to have limited processing capability of the service

Examples of acknowledgment the transmitting end

  • Add Configuration
# 消息发送到交换器确认
spring.rabbitmq.publisher-confirms=true
# 消息发送到队列确认
spring.rabbitmq.publisher-returns=true
复制代码
  • Create two classes were achieved RabbitTemplate the listener and ReturnCallback ConfirmCallback interface ConfirmCallback interface when the message is sent to the switch to achieve ReturnCallback callback interface callback specified when the message queue is less than the route
消息发送到交换机监听类
@Slf4j
@Component
public class SendConfirmCallback implements RabbitTemplate.ConfirmCallback {
   
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        if (ack) {
            log.info("Success... 消息成功发送到交换机! correlationData:{}", correlationData);
        } else {
            log.info("Fail... 消息发送到交换机失败! correlationData:{}", correlationData);
        }
    }
}

/**
 * 消息未路由到队列监听类
 * @author by peng
 * @date in 2019-06-01 21:32
 */
@Slf4j
@Component
public class SendReturnCallback implements RabbitTemplate.ReturnCallback {
    
    @Override
    public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
        log.error("Fail... message:{},从交换机exchange:{},以路由键routingKey:{}," +
                        "未找到匹配队列,replyCode:{},replyText:{}",
                message, exchange, routingKey, replyCode, replyText);
    }
}
复制代码
  • Reinjected RabbitTemplate, and set up two listeners classes
@Configuration
public class RabbitConfig {
    
    @Bean
    RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory){
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        rabbitTemplate.setMandatory(true);
        rabbitTemplate.setConfirmCallback( new SendConfirmCallback());
        rabbitTemplate.setReturnCallback( new SendReturnCallback());
        return rabbitTemplate;
    }
}
复制代码
  • The definition of producers and consumers
生产者
@Component
public class Sender {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    public void sendConfirmSuccess() {
        String content = "Message sent to exist exchange!";
        this.rabbitTemplate.convertAndSend("directConfirmExchange", "exist", content);
        System.out.println("########### SendConfirmSuccess : " + content);
    }
    
    public void sendConfirmError() {
        String content = "Message sent to not exist exchange!";
        this.rabbitTemplate.convertAndSend("notExistExchange", "exist", content);
        System.out.println("########### SendConfirmError : " + content);
    }
    
    public void sendReturn() {
        String content = "Message sent to exist exchange! But no queue to routing to";
        this.rabbitTemplate.convertAndSend("directConfirmExchange", "not-exist", content);
        System.out.println("########### SendWReturn : " + content);
    }
   
}

// 消费者
@Component
@RabbitListener(queues = "existQueue")
public class Receiver {

    @RabbitHandler
    public void process(String message) {
        System.out.println("########### Receiver Msg:" + message);
    }
}
复制代码
  • Test category
@RunWith(value=SpringJUnit4ClassRunner.class)
@SpringBootTest
public class ConfirmTest {
    @Autowired
    private Sender sender;
    @Test
    public void sendConfirmSuccess() {
        sender.sendConfirmSuccess();
        
        结果:成功发送并消费了消息,并输出监听日志
        ########### SendConfirmSuccess : Message sent to exist exchange!
        Success... 消息成功发送到交换机! correlationData:null
        ########### Receiver Msg:Message sent to exist exchange!
    }
    @Test
    public void sendConfirmError() {
        sender.sendConfirmError();
        结果:消息发送失败,并输入监听日志
        ########### SendConfirmError : Message sent to not exist exchange!
        Fail... 消息发送到交换机失败! correlationData:null
        Channel shutdown: channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no exchange 'notExistExchange' in vhost '/', class-id=60, method-id=40)
    }
    @Test
    public void sendReturn() {
        sender.sendReturn();
        结果:消息发送到交换机,但路由不到队列(不存在队列匹配路由键)
        ########### SendWReturn : Message sent to exist exchange! But no queue to routing to
        Success... 消息成功发送到交换机! correlationData:null
        Fail... message:(Body:'Message sent to exist exchange! But no queue to routing to' MessageProperties [headers={}, contentType=text/plain, contentEncoding=UTF-8, contentLength=0, receivedDeliveryMode=PERSISTENT, priority=0, deliveryTag=0]),从交换机exchange:directConfirmExchange,以路由键routingKey:not-exist,未找到匹配队列,replyCode:312,replyText:NO_ROUTE
    }
    
}
复制代码

Confirm the consumer side

  • Add Configuration
# 消费者消息确认--手动 ACK
spring.rabbitmq.listener.direct.acknowledge-mode=manual
spring.rabbitmq.listener.simple.acknowledge-mode=manual
复制代码
  • Consumer Code
@Component
@RabbitListener(queues = "existQueue")
public class AckReceiver {
    
    @RabbitHandler
    public void process(String content, Channel channel, Message message) {
        try {
            System.out.println("########### message:" + message);
            // 业务处理成功后调用,消息会被确认消费
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
            // 业务处理失败后调用
            //channel.basicNack(message.getMessageProperties().getDeliveryTag(),false, true);
            //channel.basicReject(message.getMessageProperties().getDeliveryTag(), true);
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println("########### Receiver Msg:" + content);
    }
}
复制代码

Guess you like

Origin blog.csdn.net/weixin_33694172/article/details/91370505