RabbitMQ速通--04消息可靠性投递与特殊队列

消息可靠性投递

故障概述

  • 故障情况1:消息没有发送到消息队列
    • 解决方式1:在生产者端分别针对交换机和队列来进行确认,当没能成功发送到消息队列服务器上,则尝试重新发送。
    • 解决方式2:为目标交换机指定备份交换机,当目标交换机投递失败时,把消息投递到备份交换机
  • 故障情况2:消息队列服务器宕机导致内存中消息丢失
    • 解决方式:将消息持久化(已经默认实现)
  • 故障情况3:消费端宕机或抛出异常导致消息没被成功消费
    • 解决方式:消费端消费成功后,给服务器返回ACK消息,之后消息队列服务器才会删除该消息。当消费端消费信息失败,就给服务端返回NACK信息,同时把消费恢复为待消费的状态,这样就可以再次取回消息(需要消费端接口支持幂等性)

生产者端消息确认机制

  1. 在配置文件中添加相应的配置项
  2. 在配置类中声明相应回调函数
  3. 为RabbitTemplate设置增强

配置文件

spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
    virtual-host: /
    publisher-confirm-type: CORRELATED # 交换机的确认
    publisher-returns: true # 队列的确认

配置类

@Configuration
@Slf4j
public class RabbitConfig implements RabbitTemplate.ConfirmCallback,RabbitTemplate.ReturnsCallback {

    // 从容器中取出rabbitTemplate
    @Autowired
    private RabbitTemplate rabbitTemplate;

    // 初始化rabbitTemplate后将下面两个回调添加进去
    // 该注解可以指定在对象创建后立即执行,在spring中使用时,可以保证对象在初始化之后执行该方法
    @PostConstruct
    public void initRabbitTemplate() {
        rabbitTemplate.setConfirmCallback(this);
        rabbitTemplate.setReturnsCallback(this);
    }

    // 消息发送到交换机无论成功与否都会执行该方法
    // 三个参数分别描述:相关数据,是否接收成功,接收失败的原因
    @Override
    public void confirm(CorrelationData correlationData, boolean b, String s) {
        log.info("相关数据" + correlationData);
        log.info("交换机是否接收成功" + b);
        if(!b){log.info("交换机接受失败的原因" + s);}
    }

    // 只有当发送到队列失败才会回调该方法
    // 该参数用以描述相关信息
    @Override
    public void returnedMessage(ReturnedMessage returnedMessage) {
        log.info("消息主体" + new String(returnedMessage.getMessage().getBody()));
        log.info("应答码" + returnedMessage.getReplyCode());
        log.info("应答码描述" + returnedMessage.getReplyText());
        log.info("消息使用的交换机"+returnedMessage.getExchange());
        log.info("消息使用的路由键"+returnedMessage.getRoutingKey());
    }
}

测试

@SpringBootTest
class ConfirmProducerApplicationTests {

    // 需要提前创建该队列
    public static final String EXCHANGE_DIRECT = "exchange.direct.order";
    public static final String Routing_KEY = "order";

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    void testSendMessage() {
        rabbitTemplate.convertAndSend(EXCHANGE_DIRECT, Routing_KEY, "Hello World");
    }

}

备份交换机

若目标交换机出现故障,导致生产者消息不能被成功消费消息。此时,我们通常采用备份交换机来进行故障转移,备份交换机可以绑定另外一个队列,让另一个消费者对其进行监听,用以记录错误日志方便后续人员排查

备份交换机的创建可以直接在rabbitmq面板中进行。该交换机类型必须是fanout(从目标交换机将消息转到备份交换机时不带路由键)

目标交换机的创建需要在备份交换机之后进行,因为指定备份交换只能在创建时通过点击Add Alternate exchange,并且填写备份交换机名进行。

消费者端确认机制

  1. 在配置文件中将消息确认模式改为手动确认
  2. 在消费者监听器添加相应调用
spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
    virtual-host: /
    listener:
      simple:
        acknowledge-mode: manual # 将消息确认模式改为手动

消费者

@Component
@Slf4j
public class MyMessageListener{
  public static final String QUEUE_NAME = "queue.order";

  @RabbitListener(queues = {QUEUE_NAME})
  public void processMessage(String dataString, Message message, Channel channel) throws IOException {

    // 获取当前消息的deliveryTag
    long deliveryTag = message.getMessageProperties().getDeliveryTag();

    try{
      log.info("消费端 消息内容" + dataString);
      // 返回ACK信息
      // 第一个参数为deliveryTag,可以看作是每条消息的id(就算是广播的消息也是全局唯一)
      // 第二个参数若为true时,则对此消息还有之前的全部批量操作,若为false则只会对当前消息进行操作
      channel.basicAck(deliveryTag,false);
    } catch (Exception e) {
      // 获取当前消息是否是重复投递的
      Boolean redelivered = message.getMessageProperties().getRedelivered();

      if(redelivered){channel.basicNack(deliveryTag,false,false);}

      // 返回NACK消息
      // 前两个参数同basicAck方法,第三个参数用于标定该消息是否要重新放回队列
      channel.basicNack(deliveryTag,false,true);
      throw new RuntimeException(e);
    }
  }
}

特殊队列

死信队列

  • 死信
    • 当一个消息无法被消费,则成为了死信。
  • 死信产生的原因
    • 拒绝
      • 消费者拒绝接收消息(basicNack/basicReject),并且不把消息重新放入原目标队列(requeue=false)
    • 溢出
      • 队列中消息数量达到限制,根据先进先出原则,队列中最早的消息会变成死信
    • 超时
      • 消息达到超时时间未被消费
  • 死信的处理方式
    • 丢弃
      • 对于不重要的消息直接丢弃,不做处理
    • 入库
      • 把死信写入数据库,之后进行处理
    • 监听
      • 消息变成死信后进入死信队列,并专门设置消费端监听死信队列,做后续处理

要使用死信队列,需要先创建死信队列,再创建正常队列,并且还需要在创建正常队列时指定死信交换机,队列长度,过期时间等信息

备份交换机和死信队列看起来相似,但是前者的设计目的是防止消息因路由失败而丢失,作用在路由阶段。后者设计目的是处理消费失败或延迟的消息,作用在消费阶段。

延迟队列

在rabbitMQ中要使用延迟队列,可以通过死信队列(给正常队列设置超时时间,时间一到就进入死信队列被消费),同时也可以使用插件https://github.com/rabbitmq/rabbitmq-delayed-message-exchange

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值