RabbitMQ--死信交换机

目录

一、什么是死信交换机

二、TTL

三、代码实现

        生产者(publisher)

        消费者(consumer)

        效果展示

四、延迟队列

五、消息发送确认

        5.1.发送的消息怎么样才算失败或成功?如何确认?

消息消费者如何通知 Rabbit 消息消费成功?

六、消息堆积问题

七、惰性队列


一、什么是死信交换机

        当一个队列中信息满足下列情况之一时,可以成为死信(dead letter)

  &消费者使用basic.reject(RejectAndDontRequeueRecoverer处理策略)或bastic.nack(ImmediateRequeueMessageRecoverer处理策略)消费失败,requeue(重新入队)设置参数为false;

  &消息是一个过期消息,过期无人消费;

  &要投递的队列消息堆积满了,最早的消息成为死信;

如果该队列配置了dead-letter-exchange属性,指定交换机,队列设置dead-letter-routing-key属性,设置死信交换机与死信队列的RoutingKey,那么队列中的死信就会投递到这个交换机中,而这个交换机成为死信交换机(Dead Letter Exchange,简称DLX)。

二、TTL

TTL,也就是Time-To-Live。如果队列中的消息TTL结束仍未消费则会成为死信,TTL超时分为两种情况:

  &消息所在的队列设置了存活时间;

  &消息本身设置了存活时间;

消息超时的两种方式:

  &给队列设置超时时间ttl,进入队列超过ttl的消息变为死信;

  &给消息设置超时时间,队列接受消息超过ttl时间未消费;

  &两者共存时,以ttl时间短的为准。

三、代码实现

        生产者(publisher)

        RabbitConfig

// 死信,延迟队列

@Bean
public Queue queueA(){
    Map<String,Object> config = new HashMap<>();
    //message在该队列queue的存活时间最大20秒
    config.put("x-message-ttl",20000);
    //x-dead-letter-exchange参数是设置该队列的死信交换器(DLX)
    config.put("x-dead-letter-exchange","ExchangeB");
    config.put("x-dead-letter-routing-key","bb");
    return new Queue("queueA",true,false,false,config);
}

@Bean
public DirectExchange ExchangeA(){
    return new DirectExchange("ExchangeA");
}

@Bean
public Binding bindingA(){
    return BindingBuilder
            .bind(queueA())
            .to(ExchangeA())
            .with("aa");
}

@Bean
public Queue queueB(){
    return new Queue("queueB");
}

@Bean
public DirectExchange ExchangeB(){
    return new DirectExchange("ExchangeB");
}

@Bean
public Binding bindingB(){
    return BindingBuilder
            .bind(queueB())
            .to(ExchangeB())
            .with("bb");
}

        TestController

@RequestMapping("/send5")
public String send5() throws Exception{
    template.convertAndSend("ExchangeA","aa","1232321412242");
    return "🤣";
}

        消费者(consumer)

        ReceiverQB

@Component
@SuppressWarnings("all")
@Slf4j
@RabbitListener(queues = "queueB")
public class ReceiverQB {
    @RabbitHandler
    public void process(String id) {
        log.warn("QB接收到:" + id);
        //去数据库做修改
        //更改数据库的状态为为取消(id一致且订单未支付的情况下
        // update order set status=-1 where id=#{id} and status=1
    }
}

        效果展示

 

四、延迟队列

利用ttl结合死信交换机,实现了消息发出后,消费者延迟收到消息的结果,这种消息队列成为延迟队列(Delay Queue)模式。

延迟队列使用的场景:

  &用户下单后超过15分钟未支付取消支付;

  &预约20分钟会议,到时间自动通知参会人员;

五、消息发送确认

        5.1.发送的消息怎么样才算失败或成功?如何确认?

                当消息无法路由到队列时,确认消息路由失败。消息成功路由时,当需要发送的队列都发送成功后,进行确认消息,对于持久化队列意味着写入磁盘,对于镜像队列意味着所有镜像接收成功

                通过实现 ConfirmCallback 接口,消息发送到 Broker 后触发回调,确认消息是否到达 Broker 服务器,也就是只确认是否正确到达 Exchange 中

@Component
public class RabbitTemplateConfig implements RabbitTemplate.ConfirmCallback{

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @PostConstruct
    public void init(){
        rabbitTemplate.setConfirmCallback(this);            //指定 ConfirmCallback
    }

    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        System.out.println("消息唯一标识:"+correlationData);
        System.out.println("确认结果:"+ack);
        System.out.println("失败原因:"+cause);
    }

还需要在配置文件添加配置

spring:
  rabbitmq:
    publisher-confirms: true 

  • 通过实现 ReturnCallback 接口,启动消息失败返回,比如路由不到队列时触发回调

@Component
public class RabbitTemplateConfig implements RabbitTemplate.ReturnCallback{

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @PostConstruct
    public void init(){
        rabbitTemplate.setReturnCallback(this);             //指定 ReturnCallback
    }

    @Override
    public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
        System.out.println("消息主体 message : "+message);
        System.out.println("消息主体 message : "+replyCode);
        System.out.println("描述:"+replyText);
        System.out.println("消息使用的交换器 exchange : "+exchange);
        System.out.println("消息使用的路由键 routing : "+routingKey);
    }
}

还需要在配置文件添加配置

spring:
  rabbitmq:
    publisher-returns: true 

消息消费者如何通知 Rabbit 消息消费成功?

消息通过 ACK 确认是否被正确接收,每个 Message 都要被确认(acknowledged),可以手动去 ACK 或自动 ACK

  • 自动确认会在消息发送给消费者后立即确认,但存在丢失消息的可能,如果消费端消费逻辑抛出异常,也就是消费端没有处理成功这条消息,那么就相当于丢失了消息

  • 如果消息已经被处理,但后续代码抛出异常,使用 Spring 进行管理的话消费端业务逻辑会进行回滚,这也同样造成了实际意义的消息丢失

  • 如果手动确认则当消费者调用 ack、nack、reject 几种方法进行确认,手动确认可以在业务失败后进行一些操作,如果消息未被 ACK 则会发送到下一个消费者

  • 如果某个服务忘记 ACK 了,则 RabbitMQ 不会再发送数据给它,因为 RabbitMQ 认为该服务的处理能力有限

  • ACK 机制还可以起到限流作用,比如在接收到某条消息时休眠几秒钟

  • 消息确认模式有:

    • AcknowledgeMode.NONE:自动确认

    • AcknowledgeMode.AUTO:根据情况确认

    • AcknowledgeMode.MANUAL:手动确认

六、消息堆积问题

当生产者发送的消息的速度大于消费者消费的速度,就会导致队列中的消息堆积,直到达到消息的上限,最早接受的消息,可能会被丢弃,成为死信。

解决消费堆积的三种策略:

  &增加消费者,增加消费速度

  &在消费者内开启线程池加快消息处理速度

  &扩大队列的容积

七、惰性队列

从RabbitMq的3.6.0版本开始增加了Lazy Queues的概念,也就是惰性队列。

惰性队列的特征如下:

  &接受消息后直接存入磁盘而非内存;

  &消费者消费消息才会中磁盘中将消息加载到内存;

  &支持数百万的数据存储;

  • 20
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值