springboot引入rabbitmq实现延迟队列,普通队列

本文介绍了如何在SpringBoot应用中集成RabbitMQ,包括安装RabbitMQ和延迟队列插件,以及处理消息丢失的方法。在延迟队列部分,讨论了生产者和消费者的配置,而普通消息部分则关注了FANOUT类型的交换机和路由键的使用。此外,还解决了RabbitTemplate中ConfirmCallback的限制,并提到了惰性队列的使用。

前言:

安装rabbitmq,以及插件地址:自行百度

RabbitMQ超详细安装教程(Linux)_rabbitmq安装_Baret-H的博客-优快云博客

Rabbitmq延迟队列插件安装_rabbitmq安装x-delayed_最菜Java开发实习生的博客-优快云博客

面试突击

1.消息丢失怎么处理

1.设置手动ACK,

2.设置发布者确认 ConfirmCallback,就是消息到达exchange了返回ack

3.设置到达确认,ReturnCallback--已经过期方法,就是消息到达Queue了返回ack

4.消息持久化 durable = "true"

1.引入rabbitmq依赖

        <!--amqp-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>

2.配置文件

spring:
  rabbitmq:
    addresses: 127.0.0.1:5672
    username: admin
    password: admin
    virtual-host: /
    connection-timeout: 15000
    #发布者确认,启动消息确认模式,correlated值是发布消息成功到交换器后会触发回调方法
    #开启消息确认机制 confirm 异步
    publisher-confirm-type: correlated
    #生产者确认机制,确保消息会正确发送,如果发送失败会有错误回执,从而触发重试
    #之前的旧版本 开启消息确认机制的方式
    #publisher-confirms: true
    #发布者到达确认(消息不能正确路由),设置return消息模式,注意要和mandatory一起配合使用
    publisher-returns: true
    template:
      #如果消息没有匹配上队列,则记录;第二种是在webui设置一个默认交换器进行收集处理
      mandatory: true
      #失败重试
      retry:
        #开启失败重试
        enabled: true
        #initial-interval: 10000ms第一次重试的间隔时长
        #max-interval: 300000ms最长重试间隔,超过这个间隔将不再重试
        #multiplier: 2下次重试间隔的倍数,此处是2即下次重试间隔是上次的2倍
    listener:
      simple:
        #表示消费者消费成功消息以后需要手工的进行验收(ACK),默认auto,simple关闭自动ack,手动ack
        acknowledge-mode: manual
        #线程数,消费端的监听个数(即@RabbitListener开启几个线程去处理数据)
        #concurrency: 5
        #批量消息消费
        #prefetch: 1
        #最大线程数
        #max-concurrency: 10
        #direct:
        #acknowledge-mode: manual
        retry:
          enabled: true

3.延迟消息

RabbitMQ Management

3.1.生产者

TODO:rabbitTemplate.setConfirmCallback  报错exception only one  则需要配置RabbitTemplate,后文【5】会说明

@Resource
    private RabbitTemplate rabbitTemplate;

    /**
     * 订单支付超时监听
     * @param orderTimeOutMessage
     */
    public void orderTimeOutSendMessage(OrderTimeOutMessage orderTimeOutMessage) {
        log.info("[发送MQ消息-订单30分钟未支付]成功  {}", JSON.toJSONString(orderTimeOutMessage));

        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            @Override
            public void confirm(CorrelationData correlationData, boolean ack, String cause) {
                if(ack){
                    log.info("[ConfirmCallback] [ack = true]  cause=" + cause);
                }else{
                    log.error("[ConfirmCallback] [ack = false]  cause=" + cause);
                }
            }
        });

        rabbitTemplate.convertAndSend("order.timeout.delayed", "",orderTimeOutMessage, new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
                //设置消息的持久化
                message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
                //设置延迟的时间
                //message.getMessageProperties().setDelay(threeTimeOut);
                message.getMessageProperties().setDelay(30 * 60 * 1000);
                return message;
            }
        });
    }
@Data
public class OrderTimeOutMessage implements Serializable {

    /**
     * 枚举类型
     */
    @NotNull
    private String allTypeValue;

    /**
     * 订单ID
     */
    @NotNull
    private Long orderId;
    /**
     * 订单号
     */
    @NotNull
    private String orderNo;

3.2.消费者

@Slf4j
@Component
@RequiredArgsConstructor
public class RabbitQueueOrderTimeOutDelayedListener {

        private static final String QUEUE = "order.timeout.delayed.queue";
        private static final String EXCHANGE = "order.timeout.delayed";

        @Resource
        private Service service;


        @RabbitListener(
                bindings = @QueueBinding(
                value = @Queue(value = QUEUE, durable = "true"),
                //exchange = @Exchange(value = EXCHANGE, type = ExchangeTypes.FANOUT, ignoreDeclarationExceptions = "true"),
                exchange = @Exchange(value = EXCHANGE, type = "x-delayed-message", arguments = @Argument(name = "x-delayed-type",value ="fanout"), ignoreDeclarationExceptions = "true"),
                key = "")
        )
        @RabbitHandler
        public void onMessage(OrderTimeOutMessage orderTimeOutMessage,Message message, Channel channel){
            long deliveryTag = message.getMessageProperties().getDeliveryTag();
            String body = new String(message.getBody(), StandardCharsets.UTF_8);
            log.info("订单30分钟未支付- consumer RabbitMq消息内容  {}", JSON.toJSONString(orderTimeOutMessage));
            try {

                AllTypeEnum allTypeEnum = AllTypeEnum.valueOf(orderTimeOutMessage.getAllTypeValue());
                switch (allTypeEnum){
                    case a: service.a(orderTimeOutMessage);return;
                    case b: service.b(orderTimeOutMessage);return;
                    
                    default:log.error("订单类型   {} 不存在", orderTimeOutMessage.getAllTypeValue());
                }

                //rabbitmq 的ack机制,第二个参数返回true,表示需要将这条消息投递给其他消费者重新消费
                channel.basicAck(deliveryTag, false);
            }catch (Exception e){
                e.printStackTrace();
                try {
                    //第三个参数true,表示这个消费会重新进入队列
                    channel.basicNack(deliveryTag, false, true);
                } catch (IOException ex) {
                    throw new RuntimeException(ex);
                }
            }
        }

4.普通消息

4.1生产者

本文只讲解ExchangeTypes.FANOUT类型,其他类型修改ExchangeTypes类型,在生产者设置routingKey即可,消费端设置key = {"",""}即可,没什么可说的,就是模糊匹配

routingKey简单说就是标签,设置模糊匹配的规则,类似rocketmq的tag

 public void orderSendMessage(OrderTimeOutMessage orderTimeOutMessage) {

        rabbitTemplate.setConfirmCallback(....同上);

        rabbitTemplate.convertAndSend("test.exchange", "",orderTimeOutMessage);
    }

4.2消费者

@Slf4j
@Component
@RequiredArgsConstructor
public class RabbitQueueTimeOutListener {

        private static final String QUEUE = "test.exchange.queue";
        private static final String EXCHANGE = "test.exchange";


        @RabbitListener(
                bindings = @QueueBinding(
                value = @Queue(value = QUEUE, durable = "true"),
                exchange = @Exchange(value = EXCHANGE, type = ExchangeTypes.FANOUT, ignoreDeclarationExceptions = "true"),
                key = "")
        )
        @RabbitHandler
        public void onMessage(OrderTimeOutMessage orderTimeOutMessage,Message message, Channel channel){
            long deliveryTag = message.getMessageProperties().getDeliveryTag();
            String body = new String(message.getBody(), StandardCharsets.UTF_8);
            log.info("RabbitMq消息内容 {}", body);
            try {

                //todo

                //rabbitmq 的ack机制,第二个参数返回true,表示需要将这条消息投递给其他消费者重新消费
                channel.basicAck(deliveryTag, false);
            }catch (Exception e){
                e.printStackTrace();
                try {
                    //第三个参数true,表示这个消费会重新进入队列
                    channel.basicNack(deliveryTag, false, true);
                } catch (IOException ex) {
                    throw new RuntimeException(ex);
                }
            }
        }

5.针对出现Only one ConfirmCallback is supported by each RabbitTemplate

5.1解决方法

@Configuration
@Slf4j
public class RabbitmqConfig implements RabbitTemplate.ConfirmCallback {

    @Bean
    @Scope("prototype")//作用域-多例,避免only one ConfirmCallback
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        rabbitTemplate.setMandatory(true);
        rabbitTemplate.setConfirmCallback(this);//这里是全局配置监听,也可以在每次发送的时候监听,二者取其一
        return rabbitTemplate;
    }

    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        //log.info("[ConfirmCallback] [ack = true]  correlationData=" + correlationData.toString());
        if(ack){
            log.info("[ConfirmCallback] [ack = true]  cause=" + cause);
        }else{
            log.error("[ConfirmCallback] [ack = false]  cause=" + cause);
        }
    }
}

6.惰性队列

RabbitMQ惰性队列使用_特立独行的程序员的博客-优快云博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值