浅谈RabbitMQ(二)

RabbitMQ消息的可靠性问题

由于网络、软硬件等问题,消息是存在丢失问题
在这里插入图片描述

在哪几个方面存在丢失:

  1. 生产者到交换机
  2. 交换机到队列
  3. 队列到消费者

解决方案:

  1. 针对 生产者到交换机

    confirm 机制 确定生产者将消息投递到交换机

    1. 针对 交换机到队列

    return 机制 交换机发送消息到队列失败会执行回调

    1. 针对 队列到消费者

    消息没发 --> 消息持久化

    消息发了,不知道是否被消费 —> 手动确认 ack

Confirm&Return机制

rabbitmq:
  publisher-confirm-type: correlated  # 启动confirm机制
  publisher-returns: true # 启动return机制

配置类添加


    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory){
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        //设置confirm机制的回调
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            @Override
            public void confirm(CorrelationData correlationData, boolean ack, String cause) {
                if (ack) {
                    log.info("消息{}发送到Exchange成功!!!",correlationData);
                } else {
                    log.error("消息{}发送到Exchange失败,原因:{}",correlationData,cause);
                }
            }
        });
        //设置return机制的回调
        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
        	//交换机发送消息到队列失败回调
            @Override
            public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
                log.error("消息发送到Queue失败!{}",message);
            }
        });
        return rabbitTemplate;
    }

生产者发送消息,设置消息的id

rabbitTemplate.convertAndSend(RabbitMQConfig.COURSE_EXCHANGE,
                RabbitMQConfig.KEY_COURSE_SAVE, JSON.toJSONString(course),
                new CorrelationData(UUID.randomUUID().toString()))

RabbitMQ消息幂等性

重复消费问题:

队列发送消息给消费者,因为网络原因,消费者没有收到,队列重复发送,网络恢复后会发送多个相同的消息给消费者

可能导致问题:

重复添加数据等非幂等性业务问题

非幂等性业务(每次操作获得不同结果,如:添加)

幂等性业务(每次操作结果相同,如:更新、删除)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-He0Vcpgw-1655076139048)(RabbitMQ高级.assets/image-20211230092040698.png)]

消息有唯一id,保存redis,手动确认

解决方案:

  1. 给每个消息添加id
  2. 消费者获得消息后,通过id查询redis,判断该id是否存在
  3. 如果不存在,就修改该id的value为1,执行业务,进行手动确认
  4. 如果存在,就不执行业务

Redis的setnx命令

setnx key value

如果该键不存在,就设置键和值,返回1

如果该键存在,直接返回

解决方案:

  1. 加入Redis的依赖和配置文件和配置类
rabbitmq:
  host: localhost
  port: 5672
  virtual-host: myhost
  username: admin
  password: 123456
  listener:
    simple:
      acknowledge-mode: manual  # 消费者手动确认
redis:
  host: 192.168.223.223
  port: 6379
  1. 修改监听器
@Autowired
RedisTemplate<String,Object> redisTemplate;

/**
 * 监听课程添加操作
 */
@RabbitListener(bindings = {
        @QueueBinding(value = @Queue(value = QUEUE_COURSE_SAVE, durable = "true"),
                exchange = @Exchange(value = COURSE_EXCHANGE,
                        type = ExchangeTypes.TOPIC,
                        ignoreDeclarationExceptions = "true")
                , key = KEY_COURSE_SAVE)})
public void receiveCourseSaveMessage(String json, Channel channel, Message message) throws IOException {
    log.info("课程添加:{}",json);
    ValueOperations<String, Object> ops = redisTemplate.opsForValue();
    //获得消息id
    String messageId = message.getMessageProperties().getHeader("spring_returned_message_correlation");
    log.info("消息id:{}",messageId);
    //执行setnx命令,以messageId为键保存数据,设置过期时间
    if(ops.setIfAbsent(messageId,"0",100, TimeUnit.SECONDS)){
        //如果返回true,则该键不存在,也就是没有重复消费
        //将消息转为课程,保存到es中
        Course course = JSON.parseObject(json,Course.class);
        courseService.saveOrUpdate(course);
        log.info("添加完成:{}",course);
        //修改messageId键的值为1,代表该消息已经被消费
        redisTemplate.opsForValue().set(messageId, "1",100,TimeUnit.SECONDS);
        //手动确认
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
    }else{
        //返回false,代表存在该键
        String msg = (String) ops.get(messageId);
        log.info("该消息{}已经存在{}",messageId,msg);
        if("1".equals(msg)){
            //为1代表业务已经执行过了,手动确认结束
            channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
        }
    }
}
eId,msg);
        if("1".equals(msg)){
            //为1代表业务已经执行过了,手动确认结束
            channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值