springboot下rabbitmq的使用

基本知识:

在这里插入图片描述

重要组件

  • 生产者: 生产消息并选择发送给,某个exchange(交换机),并指定路由键,路由键决定了投递给哪个队列
  • 交换机: 首先交换机本身通过路由键绑定多个队列,接收到消息后根据路由选择将消息发送到哪个队列
  • 队列: 队列接收到消息
  • 消费者: 消费者监听队列,获得消息

消息路由

  • direct :生产者发送消息时的路由键 和交换机投递消息给队列时的路由键完全一致才会进行投递,多个队列只会消费一条信息
  • fanout:生产者发送的消息,会投递给交换机绑定的所有队列,每个队列都会获得一份信息
  • topic:和direct基本一致,区别在于路由键不再要求完全一致,类似正则匹配,"#“匹配0个和多个单词,”*"匹配一个单词
  • headers:极少使用

安装

docker安装

1.docker pull rabbitmq:3.8-management
2.docker run   -p 5672:5672 -p 15672:15672  --name rabbitmq -d rabbitmq:3.8-management

登录 ip:5672,输入guest,guest即可

springboot简单整合

添加依赖

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
            <groupId>org.springframework.amqp</groupId>
            <artifactId>spring-rabbit-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

配置文件

如果不在本地安装的话,这里配置一下ip地址和端口号

spring:
  rabbitmq:
    host: new ip

创建并绑定交换机,队列

创建交换机

    @Autowired
    AmqpAdmin amqpAdmin;

    @Test
    void createExchange() {
        /**
        name : 交换机的名字
        durable: 是否持久化
        autoDelete: 当交换机上绑定的队列都消失,自动删除,如果是初次创建的话不会触发自动删除机制
        **/
        Exchange exchange = new TopicExchange("hello-topic-exchange", true, false);
        amqpAdmin.declareExchange(exchange);
    }

创建队列

    @Autowired
    AmqpAdmin amqpAdmin;

    @Test
    void createQueue() {
        /**
        name : 队列的名字
        durable :是否持久化
        exclusive : 1.只对首次声明它的连接(Connection)可见 2.会在其连接断开的时候自动删除
        autoDelete :没有消费者监听队列后,自动删除
       */
        Queue queue = new Queue("hello-topic-queue", true, false, false);
        amqpAdmin.declareQueue(queue);
    }

绑定交换机和队列

   @Autowired
    AmqpAdmin amqpAdmin;  
   @Test
    void createBinding() {
        /**
         * destination      想绑定的队列或者交换机的名字
         * destinationType  绑定类型(交换机可以和队列/也可以和交换机绑定)
         * exchange        想绑定上的某交换机的名字
         * routingKey      路由键
         */
        Binding binding = new Binding("hello-topic-queue",
                Binding.DestinationType.QUEUE,
                "hello-topic-exchange",
                "hello.java",null);     
        amqpAdmin.declareBinding(binding);
    }

也可以通过另一种方法绑定:

    @Test
    void createAll(){
        TopicExchange exchange = new TopicExchange("hello-topic-exchange", true, false);
        amqpAdmin.declareExchange(exchange);
        Queue queue = new Queue("hello-topic-queue", true, false, false);
        amqpAdmin.declareQueue(queue);
        Binding binding = BindingBuilder.bind(queue).to(exchange).with("hello.java");
        amqpAdmin.declareBinding(binding);
    }

在这里插入图片描述

发送接收消息

使用json方式传递对象

实体类

这里重要的是无参构造器和序列化

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Book implements Serializable {
    private String name;
    private String author;
    private Double price;
}
MessageConverter
@Configuration
public class MyRabbitConfig{

    @Bean
    public MessageConverter MessageConverter() {

        return new Jackson2JsonMessageConverter();
    }
   } 

发送消息

@RestController
@RequestMapping("mq")
@Slf4j
public class SendController {
    @Autowired
    private RabbitTemplate template;

    @GetMapping
    public String send() {
        log.info("开始给mq发送消息");
        Book book = Book.builder().name("java编程思想").price(88.5d).author("埃克尔").build();
        template.convertAndSend("hello-topic-exchange","hello.java",book);
        log.info("给mq发送消息结束");
        return "success";
    }
} 

接收消息

   @RabbitListener(queues = "hello-topic-queue")
    public void receiver(Book book, Message message, Channel channel) throws IOException {
        System.out.println(book.toString());
    }
@RabbitHandler

消息确认

生产者===>交换机确认

配置文件
spring.rabbitmq.publisher-confirm-type=correlated
回调函数
        template.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            @Override
            public void confirm(CorrelationData correlationData, boolean ack, String s) {
                if (ack) {
                    log.info("correlationData:{} 投递成功", correlationData);

                } else {
                    log.info("correlationData:{} ,投递失败,,reason:{}", correlationData, s);
                }
            }
        });

交换机===>队列确认

配置文件
spring.rabbitmq.publisher-returns=true
spring.rabbitmq.template.mandatory=true
回调函数

注意此回调函数只有在交换机投递队列失败的情况下才会调用

        template.setReturnsCallback(new RabbitTemplate.ReturnsCallback() {
            @Override
            public void returnedMessage(ReturnedMessage returnedMessage) {
                log.info("消息由交换机转发至队列的时候失败\n 返回码:{},回复文本:{}\n路由键:{},交换机:{}\n信息:{}",
                        returnedMessage.getReplyCode(), returnedMessage.getReplyText(),
                         returnedMessage.getRoutingKey(), returnedMessage.getExchange(),
                        returnedMessage.getMessage());
            }
        });

队列 ===>消费者确认

配置文件
spring.rabbitmq.listener.simple.acknowledge-mode=manual
回调函数

注意此回调函数只有在交换机投递队列失败的情况下才会调用

    @RabbitListener(queues = "hello-topic-queue")
    public void receiver(Book book, Message message, Channel channel) throws IOException {
        System.out.println(book.toString());
        log.info("消费得到数据");
        //第二个参数代表批量操作,deliveryTag是rabbitmq自动打上的一个自增的标签,当选择可以批量操作的时候,意味着本消息,以及本消息之前的编号的消息都能接收
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
        //basicNack 表示拒绝数据,第二个参数代表 批量操作 第三个参数代表拒绝的数据是否重新入队 
        //basicReject :和basicNack功能一样,只是不能批量操作
//        channel.basicNack(deliveryTag,false,false);
//        channel.basicReject(deliveryTag,true);
    }

Only one ConfirmCallback is supported by each RabbitTemplate

此错误的原因很清楚,单个rabbitTemplate只允许一个ConfirmCallback对象进行处理,所以问题很好解决:

ConfirmCallBackService.java

@Slf4j
@Component
public class ConfirmCallBackService implements RabbitTemplate.ConfirmCallback {
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String s) {
        if (ack) {
            log.info("correlationData:{} 投递成功", correlationData);

        } else {
            log.info("correlationData:{} ,投递失败,,reason:{}", correlationData, s);
        }
    }
}

ReturnCallBackService.java

@Slf4j
@Component
public class ReturnCallBackService implements RabbitTemplate.ReturnsCallback {
    @Override
    public void returnedMessage(ReturnedMessage returnedMessage) {
        log.info("消息由交换机转发至队列的时候失败\n 返回码:{},回复文本:{}\n路由键:{},交换机:{}\n信息:{}",
                returnedMessage.getReplyCode(), returnedMessage.getReplyText(),
                returnedMessage.getRoutingKey(), returnedMessage.getExchange(),
                returnedMessage.getMessage());
    }
}

Controller

    @Autowired
    ConfirmCallBackService confirmCallBackService;
    @Autowired
    ReturnCallBackService returnCallBackService;    
    @GetMapping("/queue")
    public String sendTo(){
        Book book = Book.builder().name("java编程思想").price(88.5d).author("埃克尔").build();
        template.convertAndSend("hello-topic-exchange", "hello.java", book, new CorrelationData(UUID.randomUUID().toString()));
        template.setConfirmCallback(confirmCallBackService);
        template.setReturnsCallback(returnCallBackService);
        return "success";
    }

延迟队列

RabbitMQ 实现延迟队列 (juejin.cn)

原理

首先我们了解几个原理:

队列中的消息怎么成为死信:

  • 原队列消息长度到达限制
  • 原队列存在消息过期设置,消息到达超时时间未被消费
  • 消费者拒接消费消息,basicNack/basicReject,并且不把消息重新放入原目标队列,requeue=false

消息成为死信后如何投递到死信队列中:

  • 给原队列设置参数: x-dead-letter-exchange 和 x-dead-letter-routing-key

之后我们就可以通过这些原理来构建延迟队列了:

信息投递==>延迟交换机===>死信队列===>延时===>死信交换机=>延时队列==>消费者监听

代码

    @Test
    void createDelay() {
        String delayExchangeName="delay-exchange";
        String delayQueueName="delay-queue";
        String deadExchangeName="dead-exchange";
        String deadQueueName="dead-queue";
        String queueToExchangeKey="delay.queue.to.dead.exchange";
        String productToExchangeKey="product.to.exchange";
        
        //  创建延时交换机
        DirectExchange delayExchange = new DirectExchange(delayExchangeName, true, false);
        amqpAdmin.declareExchange(delayExchange);
        //  创建死信队列
        Map<String, Object> args = new <String, Object>HashMap(16);
        //设置消息过期之后的死信交换机(真正消费的交换机)
        args.put("x-dead-letter-exchange", deadExchangeName);
        //设置消息过期之后死信队列的路由(真正消费的路由)
        args.put("x-dead-letter-routing-key",queueToExchangeKey);
        //设定消息的TTL,单位为ms,在这里指的是60s
        args.put("x-message-ttl", 60000);
        Queue deadQueue = new Queue(deadQueueName, true, false, false,args);
        amqpAdmin.declareQueue(deadQueue);
       //创建死信交换机
        DirectExchange deadExchange = new DirectExchange(deadExchangeName, true, false);
        amqpAdmin.declareExchange(deadExchange);
       //创建延时队列
        Queue delayQueue = new Queue(delayQueueName, true, false, false);
        amqpAdmin.declareQueue(delayQueue);

        //将延时交换机和死信队列绑定
        Binding binding = BindingBuilder.bind(deadQueue).to(delayExchange).with(productToExchangeKey);
        amqpAdmin.declareBinding(binding);
        //将死信交换机和延时队列绑定
        Binding deadBinding = BindingBuilder.bind(delayQueue).to(deadExchange).with(queueToExchangeKey);
        amqpAdmin.declareBinding(deadBinding);
    }

之后我们按照之前的布置配置生产者和消费者,就可以实现延时队列这一功能了

信息TTL

上述代码中,我们使用了队列的TTL,当超过60s消息自动过期,之后投递给死信交换机,在生产者投递信息的时候我们还可以设定消息的TTL,当消息TTL达到设定之后,可以提前进入死信交换机

        template.convertAndSend("delay-exchange", "product.to.exchange", book, new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
                //这里设定为30s
                message.getMessageProperties().setExpiration("30000");
                return message;
            }
        });

经过测验,30s之后此消息就会被消费

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot集成RabbitMQ可以通过以下步骤完成: 1. 添加Maven依赖:在pom.xml文件中添加RabbitMQ的Spring Boot Starter依赖。 ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency> ``` 2. 配置RabbitMQ连接信息:在application.properties(或application.yml)文件中配置RabbitMQ的连接信息。 ```properties spring.rabbitmq.host=your_rabbitmq_host spring.rabbitmq.port=your_rabbitmq_port spring.rabbitmq.username=your_rabbitmq_username spring.rabbitmq.password=your_rabbitmq_password ``` 3. 创建RabbitMQ发送者:创建一个发送消息的类,使用`RabbitTemplate`发送消息到指定的交换机和队列。 ```java import org.springframework.amqp.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class RabbitMQSender { @Autowired private RabbitTemplate rabbitTemplate; public void sendMessage(String exchange, String routingKey, Object message) { rabbitTemplate.convertAndSend(exchange, routingKey, message); } } ``` 4. 创建RabbitMQ接收者:创建一个接收消息的类,使用`@RabbitListener`注解监听指定的队列,处理接收到的消息。 ```java import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; @Component public class RabbitMQReceiver { @RabbitListener(queues = "your_queue_name") public void receiveMessage(Object message) { // 处理接收到的消息 System.out.println("Received message: " + message.toString()); } } ``` 5. 发送和接收消息:在需要发送或接收消息的地方调用对应的方法。 ```java @Autowired private RabbitMQSender rabbitMQSender; public void sendMessage() { rabbitMQSender.sendMessage("your_exchange_name", "your_routing_key", "Hello, RabbitMQ!"); } ``` 以上是基本的使用方式,你可以根据实际需求进行扩展和配置。注意,你还需要安装并启动RabbitMQ服务。 希望对你有所帮助!如果有任何疑问,请随时提问。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值