【面试必备,SpringBoot:RabbitMQ-延迟队列

spring.rabbitmq.listener.simple.acknowledge-mode=manual

# [](https://gitee.com/vip204888/java-p7)具体编码

## [](https://gitee.com/vip204888/java-p7)定义队列

如果手动创建过或者`RabbitMQ`中已经存在该队列那么也可以省略下述代码…

package com.battcn.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

/**

  • RabbitMQ配置

  • @author Levin

  • @since 2018/4/11 0011
    */
    @Configuration
    public class RabbitConfig {

    private static final Logger log = LoggerFactory.getLogger(RabbitConfig.class);

    @Bean
    public RabbitTemplate rabbitTemplate(CachingConnectionFactory connectionFactory) {
    connectionFactory.setPublisherConfirms(true);
    connectionFactory.setPublisherReturns(true);
    RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
    rabbitTemplate.setMandatory(true);
    rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> log.info(“消息发送成功:correlationData({}),ack({}),cause({})”, correlationData, ack, cause));
    rabbitTemplate.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> log.info(“消息丢失:exchange({}),route({}),replyCode({}),replyText({}),message:{}”, exchange, routingKey, replyCode, replyText, message));
    return rabbitTemplate;
    }

    /**

    • 延迟队列 TTL 名称
      /
      private static final String REGISTER_DELAY_QUEUE = “dev.book.register.delay.queue”;
      /
      *
    • DLX,dead letter发送到的 exchange
    • TODO 此处的 exchange 很重要,具体消息就是发送到该交换机的
      /
      public static final String REGISTER_DELAY_EXCHANGE = “dev.book.register.delay.exchange”;
      /
      *
    • routing key 名称
    • TODO 此处的 routingKey 很重要要,具体消息发送在该 routingKey 的
      */
      public static final String DELAY_ROUTING_KEY = “”;

    public static final String REGISTER_QUEUE_NAME = “dev.book.register.queue”;
    public static final String REGISTER_EXCHANGE_NAME = “dev.book.register.exchange”;
    public static final String ROUTING_KEY = “all”;

    /**

    • 延迟队列配置
    • 1、params.put(“x-message-ttl”, 5 * 1000);
    • TODO 第一种方式是直接设置 Queue 延迟时间 但如果直接给队列设置过期时间,这种做法不是很灵活,(当然二者是兼容的,默认是时间小的优先)
    • 2、rabbitTemplate.convertAndSend(book, message -> {
    • message.getMessageProperties().setExpiration(2 * 1000 + “”);
    • return message;
    • });
    • TODO 第二种就是每次发送消息动态设置延迟时间,这样我们可以灵活控制
      **/
      @Bean
      public Queue delayProcessQueue() {
      Map<String, Object> params = new HashMap<>();
      // x-dead-letter-exchange 声明了队列里的死信转发到的DLX名称,
      params.put(“x-dead-letter-exchange”, REGISTER_EXCHANGE_NAME);
      // x-dead-letter-routing-key 声明了这些死信在转发时携带的 routing-key 名称。
      params.put(“x-dead-letter-routing-key”, ROUTING_KEY);
      return new Queue(REGISTER_DELAY_QUEUE, true, false, false, params);
      }

    /**

    • 需要将一个队列绑定到交换机上,要求该消息与一个特定的路由键完全匹配。
    • 这是一个完整的匹配。如果一个队列绑定到该交换机上要求路由键 “dog”,则只有被标记为“dog”的消息才被转发,不会转发dog.puppy,也不会转发dog.guard,只会转发dog。
    • TODO 它不像 TopicExchange 那样可以使用通配符适配多个
    • @return DirectExchange
      */
      @Bean
      public DirectExchange delayExchange() {
      return new DirectExchange(REGISTER_DELAY_EXCHANGE);
      }

    @Bean
    public Binding dlxBinding() {
    return BindingBuilder.bind(delayProcessQueue()).to(delayExchange()).with(DELAY_ROUTING_KEY);
    }

    @Bean
    public Queue registerBookQueue() {
    return new Queue(REGISTER_QUEUE_NAME, true);
    }

    /**

    • 将路由键和某模式进行匹配。此时队列需要绑定要一个模式上。
    • 符号“#”匹配一个或多个词,符号“”匹配不多不少一个词。因此“audit.#”能够匹配到“audit.irs.corporate”,但是“audit.” 只会匹配到“audit.irs”。
      **/
      @Bean
      public TopicExchange registerBookTopicExchange() {
      return new TopicExchange(REGISTER_EXCHANGE_NAME);
      }

    @Bean
    public Binding registerBookBinding() {
    // TODO 如果要让延迟队列之间有关联,这里的 routingKey 和 绑定的交换机很关键
    return BindingBuilder.bind(registerBookQueue()).to(registerBookTopicExchange()).with(ROUTING_KEY);
    }

}

## 实体类

创建一个`Book`类

public class Book implements java.io.Serializable {

private static final long serialVersionUID = -2164058270260403154L;

private String id;
private String name;
// 省略get set ...

}

## 控制器

编写一个`Controller`类,用于消息发送工作,同时为了看到测试效果,添加日志输出,将发送消息的时间记录下来..

package com.battcn.controller;

import com.battcn.config.RabbitConfig;
import com.battcn.entity.Book;
import com.battcn.handler.BookHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.AbstractJavaTypeMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDateTime;

/**

  • @author Levin

  • @since 2018/4/2 0002
    */
    @RestController
    @RequestMapping(value = “/books”)
    public class BookController {

    private static final Logger log = LoggerFactory.getLogger(BookController.class);

    private final RabbitTemplate rabbitTemplate;

    @Autowired
    public BookController(RabbitTemplate rabbitTemplate) {
    this.rabbitTemplate = rabbitTemplate;
    }

    /**

    • this.rabbitTemplate.convertAndSend(RabbitConfig.REGISTER_DELAY_EXCHANGE, RabbitConfig.DELAY_ROUTING_KEY, book); 对应 {@link BookHandler#listenerDelayQueue}
      */
      @GetMapping
      public void defaultMessage() {
      Book book = new Book();
      book.setId(“1”);
      book.setName(“一起来学Spring Boot”);
      // 添加延时队列
      this.rabbitTemplate.convertAndSend(RabbitConfig.REGISTER_DELAY_EXCHANGE, RabbitConfig.DELAY_ROUTING_KEY, book, message -> {
      // TODO 第一句是可要可不要,根据自己需要自行处理
      message.getMessageProperties().setHeader(AbstractJavaTypeMapper.DEFAULT_CONTENT_CLASSID_FIELD_NAME, Book.class.getName());
      // TODO 如果配置了 params.put(“x-message-ttl”, 5 * 1000); 那么这一句也可以省略,具体根据业务需要是声明 Queue 的时候就指定好延迟时间还是在发送自己控制时间
      message.getMessageProperties().setExpiration(5 * 1000 + “”);
      return message;
      });
      log.info("[发送时间] - [{}]", LocalDateTime.now());
      }
      }
## 消息消费者

默认情况下 `spring-boot-data-amqp` 是自动`ACK`机制,就意味着 MQ 会在消息消费完毕后自动帮我们去ACK,这样依赖就存在这样一个问题:**如果报错了,消息不会丢失,会无限循环消费,很容易就吧磁盘空间耗完,虽然可以配置消费的次数但这种做法也有失优雅。目前比较推荐的就是我们手动ACK然后将消费错误的消息转移到其它的消息队列中,做补偿处理。** 由于我们需要手动控制`ACK`,因此下面监听完消息后需要调用`basicAck`通知`rabbitmq`消息已被正确消费,可以将远程队列中的消息删除

知其然不知其所以然,大厂常问面试技术如何复习?

1、热门面试题及答案大全

面试前做足功夫,让你面试成功率提升一截,这里一份热门350道一线互联网常问面试题及答案助你拿offer

面试宝典+书籍+核心知识获取:戳这里免费下载!诚意满满!!!

2、多线程、高并发、缓存入门到实战项目pdf书籍

3、文中提到面试题答案整理

4、Java核心知识面试宝典

覆盖了JVM 、JAVA集合、JAVA多线程并发、JAVA基础、Spring原理、微服务、Netty与RPC、网络、日志、Zookeeper、Kafka、RabbitMQ、Hbase、MongoDB 、Cassandra、设计模式、负载均衡、数据库、一致性算法 、JAVA算法、数据结构、算法、分布式缓存、Hadoop、Spark、Storm的大量技术点且讲解的非常深入

e、MongoDB 、Cassandra、设计模式、负载均衡、数据库、一致性算法 、JAVA算法、数据结构、算法、分布式缓存、Hadoop、Spark、Storm的大量技术点且讲解的非常深入**

[外链图片转存中…(img-ySJM8lkG-1628226749206)]

[外链图片转存中…(img-6eOtnFRp-1628226749206)]

[外链图片转存中…(img-n8hDmPDF-1628226749207)]

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值