rabbitmq中的消息确认

参考地址:https://blog.csdn.net/qq_35387940/article/details/100514134
生产者中的确认主要是通过两个方法来确认的
ConfirmCallback 与 ReturnsCallback 具体什么场景会触发这两个方法,通过代码来测试下
流程:编写配置类

@SpringBootConfiguration
@Slf4j
public class RabbitConfig {


    @Bean
    public RabbitTemplate createRabbitTemplate(ConnectionFactory connectionFactory){
        RabbitTemplate rabbitTemplate = new RabbitTemplate();
        //rabbitmq注入一个连接工厂
        rabbitTemplate.setConnectionFactory(connectionFactory);
        //设置为强制性的,不关是否发送成功都需要确认
        rabbitTemplate.setMandatory(true);

        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {

            @Override
            public void confirm(CorrelationData correlationData, boolean b, String s) {
               //这里主要是对confirmCallback方法的调用
                log.info("confirmCallback: 中的相关数据为:{}", correlationData);
                log.info("调用方法之后的确认情况为:{}", b);
                log.info("调用方法失败时的原因为:{}", s);
            }
        });

        rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback() {
            @Override
            public void returnedMessage(ReturnedMessage returnedMessage) {
                log.info("returnCallBack中的消息为:{}", returnedMessage);
            }
        });
        return  rabbitTemplate;
    }

    /**
     * 这两个方法其实就是对于rabbitmq服务的响应
     * 1:消息推送到server 但在server中找不到交换机  这样就只会触发confirm方法
     * 2:消息推送到server 但在server中找不到队列
     * 3:消息推送到server,但在server中找不到交换机与队列
     * 4:消息推送成功
     */


    /**
     * 测试一个没有队列的交换机
     * @return
     */
    @Bean
    DirectExchange create(){
        return new DirectExchange("noqueue");
    }


    /**
     * 直连的队列名称
     * @return
     */
    @Bean
    public Queue testDirectQueue(){
        /**
         * dureable:是否持久化,默认是false, 持久化队列: 会被存储在磁盘上,当消息代理重启时仍然存在
         * exclusive: 默认也是false,只能被当前创建的连接使用,而且当连接关闭后队列会被关闭
         * autoDelete:是否自动删除,当没有生产者或者消费者使用队列,该队列会自动删除
         * return new Queue("testDirectQueue", true, true, fasle,)
         */
        return new  Queue("TestDirectQueue", true);
    }


    /**
     * 直连交换机
     * @return
     */
    @Bean
    DirectExchange testDirectExchange(){
        return new DirectExchange("testDirectExchange", true, false);
    }

    /**
     * 指定直连的队列与交换机,并且指定routyKey为:testDirectRouting
     * @return
     */
    @Bean
    Binding bindingDirect(){
        return BindingBuilder.bind(testDirectQueue()).to(testDirectExchange()).with("testDirectRouting");
    }

访问controller层代码编写来测试:

@RestController
@Api
public class RabbitAckControlelr {

    @Autowired
    RabbitTemplate rabbitTemplate;


    /**
     * 凭空造一个交换机,但是路由键是可以存在的是就
     * 只会触发一个ConfirmCallBack这个方法
     * @return
     */
    @RequestMapping(value = "noexchange", method = RequestMethod.GET)
    public String noexchange(){
        //现在发送一个map的消息过去
        String messageId = UUID.randomUUID().toString();
        String messageData = "test topic message2, hello";
        String format = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        Map<String, Object> map = new HashMap<>();
        map.put("messageId", messageId);
        map.put("messageData", messageData);
        map.put("createTime", format);
        rabbitTemplate.convertAndSend("noExchange", "topic.women", map);
        return "ok";
    }


    /**
     * 这是对有交换机但是没有绑定的队列的测试
     * 测试结果显示它都会触发一个ConfirmCallBack与ReturnCallBack方法
     * 注意也只有在这种情况下,才会返回全部的数据,其余的情况都是不会返回数据的
     * @return
     */
    @RequestMapping(value = "noQueue", method = RequestMethod.GET)
    public String noQueue(){
        //现在发送一个map的消息过去
        String messageId = UUID.randomUUID().toString();
        String messageData = "test topic message2, hello";
        String format = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        Map<String, Object> map = new HashMap<>();
        map.put("messageId", messageId);
        map.put("messageData", messageData);
        map.put("createTime", format);
        rabbitTemplate.convertAndSend("noqueue", "noqueue", map);
        return "ok";
    }


    /**
     * 这是测试往交换机与队列中发送消息时,没有交换机与队列绑定的情况下
     * 测试结果显示,这种情况也只是会调用confirmCallBack方法
     * @return
     */
    @RequestMapping(value = "noThing", method = RequestMethod.GET)
    public String noThing(){
        //现在发送一个map的消息过去
        String messageId = UUID.randomUUID().toString();
        String messageData = "test topic message2, hello";
        String format = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        Map<String, Object> map = new HashMap<>();
        map.put("messageId", messageId);
        map.put("messageData", messageData);
        map.put("createTime", format);
        rabbitTemplate.convertAndSend("no", "noqueue", map);
        return "ok";
    }


    @RequestMapping(value = "success", method = RequestMethod.GET)
    public String success(){
        //现在发送一个map的消息过去
        String messageId = UUID.randomUUID().toString();
        String messageData = "test topic message2, hello";
        String format = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        Map<String, Object> map = new HashMap<>();
        map.put("messageId", messageId);
        map.put("messageData", messageData);
        map.put("createTime", format);
        rabbitTemplate.convertAndSend("topicExchange", "topic.women", map);
        return "success";
    }

下面就是消费者消息确认的代码展示,rabbitmq中的消息就是自动确认的,也就是说当消息调用之后,不管消费成功都会默认已经成功消费,将消息直接丢弃了
下面是手动确认的代码展示:

@SpringBootConfiguration
public class MessageListenerConfig {


    @Autowired
    private CachingConnectionFactory cachingConnectionFactory;

    //消息处理类
    @Autowired
    private MessageListener messageListener;

    /**
     * 这是设置消息监听类
     * @return
     */
    @Bean
    public SimpleMessageListenerContainer simpleMessageListenerContainer(){
        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(cachingConnectionFactory);
        container.setConcurrentConsumers(1);    //设置当前消费者数量
        container.setMaxConcurrentConsumers(1);
        container.setAcknowledgeMode(AcknowledgeMode.MANUAL); //设置当前的消费者确认为手动的,rabbit默认的是自动的
        container.setQueueNames("topic.women");     //将队列注入到listenerContainer中去
        container.setMessageListener(messageListener);  //将消息监听类注入到容器中去
        return container;
    }

消费者中的消息处理类的代码展示如下:

@Component
@Slf4j
public class MessageListener implements ChannelAwareMessageListener {

    @Override
    public void onMessage(Message message, Channel channel) throws Exception {
        //从消息中获取到分发标记数
        long deliverTag = message.getMessageProperties().getDeliveryTag();
        try{
            String msg = message.toString();
            String[] msgArray = msg.split("'");
            Map<String, String> stringStringMap = mapStringToMap(msgArray[1].trim(), 3);
            String messageId=stringStringMap.get("messageId");
            String messageData=stringStringMap.get("messageData");
            String createTime=stringStringMap.get("createTime");
            log.info("接收到的消息为:{}, 数据为:{}, 数据创建时间为:{}", messageId, messageData, createTime);
            /**
             * channel.basicAck用于肯定消费
             * basic.nack用于否定确认(注意:这是AMQP 0-9-1的RabbitMQ扩展)
             * basic.reject用于否定确认,但与basic.nack相比有一个限制:一次只能拒绝单条消息
             * 记住basic.nack是允许消费多条消息的,具体的就是它消费的是大于当前信道中的tagid,小于的都不消费
             */
            channel.basicAck(deliverTag, true);
        }catch (Exception e){
            /**
             * 在消费时候出现异常时,常做出的处理就是
             * 消费端知道这个消息是错误的,直接丢弃
             * 不会重新将它发送到队列中去
             */
            channel.basicReject(deliverTag, false);
            e.printStackTrace();
        }
    }


    /**
     * 将一个Map String类型转换成Map
     * @param str
     * @param entryNum
     * @return
     */
    private Map<String, String> mapStringToMap(String str, int entryNum){
        String substring = str.substring(1, str.length() - 1);
        String[] split = substring.split(",", entryNum);
        Map<String, String> map = new HashMap<>();
        for(String s : split){
            String trim = s.split("=")[0].trim();
            String value = s.split("=")[1];
            map.put(trim, value);
        }
        return map;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值