rabbitmq生产者可靠消费者确认幂等性

生产者可靠

某些情况会发送失败,交换机写错了,key写错了等等

mq有回退模式和确认模式,回退模式return当放入队列中会调用return回调,确认模式confirm不保证放入到队列,当放入broker(进入mq服务)交换机就会调用confirm回调返回ack,unack

image-20241014161406641

开启确认机制:
image-20241014161508950

image-20241014161924212

编写回调方法,然后就可以记录日志或者把这条消息放到db,并保存错误原因

image-20241014161704866

消费者可靠

确认机制

Mq由消费者确认机制:
image-20241014152126373

默认是none,发到了直接销毁

image-20241014152143153

mq默认消费者收到消息就销毁消息是不安全的,所以使用手动ack的方式告诉Mq我成功了,可以销毁消息了

首先新建一个测试队列test.queue

生产者:

image-20241014155331342

消费者:

image-20241014153543022

此时所有消息都会发生异常,然后重新投递到队首,也就是说,即使你就往这个队列发了1条消息,那么由于发生异常会重新入队,且在队头,那么又继续消费这条消息,又会发生异常,就会发生死循环。

手动nack,指定死信交换机

于是针对这个问题,我们可以将该队列绑定一个死信交换机,设置requeue参数(第三参数)为false,那么就不会重新入队,转而投递到死信交换机

死信交换机设置,首先设置好死信交换机和队列dlx.direct,dlx.queue,并指定key(我这里就是设置的dead),然后在创建队列时绑定死信交换机,并指定新的key(dead)以在私信交换机中路由

image-20241014154506888

手动nack

image-20241014155434769

这样所有失败的消息都会发到死信队列,然后就可以搞一个消费者去消费这些异常信息,比如可以将信息放入db,定时任务重新发送,然后重新发送也可能再失败,那么加个字段重试次数,达到一定次数就报警,人工处理

使用task表控制幂等

加一个task表,消费时,先往task插入消息,初始status为create,且msg_id加唯一索引,一旦重复消费就抛出异常处理,接着就执行业务代码,如果没有问题就标记task状态为success,一旦有问题就标记为fail,然后人工处理

image-20241015110706443

redis实现短时间重试

@Slf4j
@Component
public class TestListener {

    @Resource
    RedissonClient redissonClient;
    @RabbitListener(queues = "test.queue")
    public void receive(Message message, Channel channel) throws Exception {
        //SolutionEnum.RETRY.process(message,channel,redissonClient);
        SolutionEnum.DIEMSG.process(message,channel,redissonClient);

    }
}
@Getter
@AllArgsConstructor
@NoArgsConstructor
@Slf4j
enum SolutionEnum{

    RETRY(1,"重试"){
        @Override
        public void process(Message message, Channel channel,RedissonClient redissonClient) throws Exception {
            String jsonStr=new String(message.getBody(),"UTF-8");
            Msg bean = JSONUtil.toBean(jsonStr, Msg.class);
            Long id = bean.getMsgId();
            log.info("消息:{}",jsonStr);
            try{
                //redis校验幂等
                boolean lock = redissonClient.getBucket("mq_msg_lock:msg_id:" + id).setIfAbsent(1, Duration.ofSeconds(5));
                if(lock){
                    //业务
                    int i=1/0;
                }else{
                    log.info("重复消费");
                }
            }catch (Exception e){
                //重试前判断
                Long res= redissonClient.getAtomicLong("mq_error_message_retry_times:message_id:"+id).incrementAndGet();
                redissonClient.getAtomicLong("mq_error_message_retry_times:message_id:"+id).expire(Duration.ofSeconds(10));
                log.error("当前重试次数:{}",res-1);
                if(res>=5+1){//重试5次
                    MessageProperties messageProperties = message.getMessageProperties();
                    log.error("重试失败,消息:{},exchange:{},key:{},队列:{}",
                            jsonStr,
                            messageProperties.getReceivedExchange(),messageProperties.getReceivedRoutingKey(),
                            messageProperties.getConsumerQueue());
                    //直接ack
                    channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
                    //保存db,告警
                }else{
                    //继续重试
                    redissonClient.getBucket("mq_msg_lock:msg_id:" + id).delete();//释放锁,否则重试不了
                    channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
                }
            }
        }
    },
    DIEMSG(2,"走死信交换机"){
        @Override
        public void process(Message message, Channel channel,RedissonClient redissonClient) throws Exception {

            try{
                //业务
                int i=1/0;
            }catch (Exception e){
                channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
            }

        }
    };
    private int code;
    private String desc;
    public abstract void process(Message message, Channel channel,RedissonClient redissonClient)  throws Exception;
}

幂等处理

各种各样的原因,可能会导致短时间内一条消息重复消费多次,那么此时我们就需要在消息内加一个唯一标识,然后在消费的时候,先加锁,保证在短时间内这条消息只会被消费一次,如果是插入操作那么可以在db加唯一索引

image-20241014160558623

image-20241014160546703

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值