RabbitMQ笔记

本篇主要记录内容:docker安装rabbitmq、rabbitmq 交换机directExchange、fanoutExchange、topicExchange三种学习,还有其他的(header)用的不多就不记录了。消息确认、延迟、死信队列等。

1:docker安装rabbitmq

//获取镜像
1:docker pull rabbitmq
//创建并启动容器
2:docker run -d --hostname my-rabbit --name rabbit -p 15672:15672 -p 5672:5672 rabbitmq

 

 

 到此就rabbitmq环境就准备完成了,不得不说docker安装真是简单。。。

2:与springboot整合配置测试环境

引入依赖

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

添加配置

spring:
   rabbitmq:
          host: 127.0.0.1
          port: 5672
          username: admin
          password: admin
          virtual-host: /

3:directExchange(直连交换机,根据路由key进行分发)

代码中配置队列、也可以采用注解方式配置、也可在rabbit控制台操作(最好在代码中配置)

1:声明交换机
2:声明队列
3:创建绑定Key
4:bind队列与交换机

DirectConfig 

package com.person.chenpt.config.rabbit;

import com.person.chenpt.constans.RabbitConst;
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 操作rabbitmq消息队列步骤
 * 代码中配置、也可以采用注解方式配置、也可在rabbit控制台操作(最好在代码中配置)
 * 1:声明交换机
 * 2:声明队列
 * 3:创建绑定Key
 * 4:bind队列与交换机
 *
 *
 * direct 直连交换机  根据路由key进行分发  多个队列binding同一个交换机
 * 配置多台监听绑定到同一个直连交互的同一个队列
 * 会以轮询的方式对消息进行消费,而且不存在重复消费。
 *
 *
 * @Author: chenpt
 * @Description:
 * @Date: Created in 2022-07-28 11:08
 * @Modified By:
 */
@Configuration
public class DirectConfig {

    @Bean
    public DirectExchange directExchange(){
        return new DirectExchange(RabbitConst.directExchange,true,false,null);
    }

    @Bean
    public Queue directQueue(){
        /**
         * durable:是否持久化,默认是false,持久化队列:会被存储在磁盘上,当消息代理重启时仍然存在,暂存队列:当前连接有效
         * autoDelete:是否自动删除,当没有生产者或者消费者使用此队列,该队列会自动删除。
         */
        return new Queue(RabbitConst.directQueue,true);
    }

    @Bean
    public Binding bindingDirectQueue(){
        return BindingBuilder.bind(directQueue()).to(directExchange()).with(RabbitConst.directRoutingKey);
    }

    @Bean
    public Queue directQueue2(){
        return new Queue(RabbitConst.directQueue2,true);
    }

    @Bean
    public Binding bindingDirectQueue2(){
        return BindingBuilder.bind(directQueue2()).to(directExchange()).with(RabbitConst.directRoutingKey);
    }


}

controller

@RestController
@RequestMapping("/rabbit")
@Api(tags = "消息队列")
public class RabbitMqController {
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @GetMapping("/send")
    @ApiOperation("direct队列发送")
    public Result sendMessage(){    
      rabbitTemplate.convertAndSend(RabbitConst.directExchange,RabbitConst.directRo    utingKey,"hello test");
      return Result.success();
    }    
}

consumer

@RabbitListener(queues = RabbitConst.directQueue2)
public void process2(String testMsg){
   System.out.println("DirectReceiver2消费者收到消息  : " + testMsg);
}

4:FanoutExchange(扇形交换机)  

不需要路由键、多个队列binding到此交换机均能收到消息

package com.person.chenpt.config.rabbit;

import com.person.chenpt.constans.RabbitConst;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 *
 * 扇型交换机
 * 不需要路由键  多个队列binding到交换机都能收到消息
 * 与多个队列采用同一个路由键binding到direct交换机效果相同
 *
 *
 * @Author: chenpt
 * @Description:
 * @Date: Created in 2022-07-28 14:54
 * @Modified By:
 */
@Configuration
public class FanoutConfig {

    @Bean
    FanoutExchange fanoutExchange(){
        return new FanoutExchange(RabbitConst.fanoutExchange,true,false);
    }

    @Bean
    Queue fanoutQueue1(){
        return new Queue(RabbitConst.fanoutQueue1,true);
    }

    @Bean
    Queue fanoutQueue2(){
        return new Queue(RabbitConst.fanoutQueue2,true);
    }

    @Bean
    Binding fanoutBinding1(){
        return BindingBuilder.bind(fanoutQueue1()).to(fanoutExchange());
    }

    @Bean
    Binding fanoutBinding2(){
        return BindingBuilder.bind(fanoutQueue2()).to(fanoutExchange());
    }

}

controller

  @GetMapping("/sendFanout")
   @ApiOperation("fanout队列发送")
   public Result sendFanoutMessage(){
      for(int i=1;i<=10;i++){
          rabbitTemplate.convertAndSend(RabbitConst.fanoutExchange,null,"hello fanout test");
      }
      return Result.success("ok");
    }

consume

    @RabbitListener(queues = RabbitConst.fanoutQueue1)
    public void fanoutQueue1(String testMsg){
        System.out.println("fanoutReceiver1消费者收到消息  : " + testMsg);
    }

    @RabbitListener(queues = RabbitConst.fanoutQueue2)
    public void fanoutQueue2(String testMsg){
        System.out.println("fanoutReceiver2消费者收到消息  : " + testMsg);
    }

5:TopicExchange(主题交换机) 

对路由键进行模式匹配后投递

符号 # 表示一个或多个词,符号 * 表示一个词。
例如“abc.#”能够匹配到“abc.def.ghi”,但是“abc.*” 只会匹配到“abc.def”
package com.person.chenpt.config.rabbit;

import com.person.chenpt.constans.RabbitConst;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 主题模式
 * 主题交换机,对路由键进行模式匹配后进行投递,
 * 符号 # 表示一个或多个词,
 * 符号 * 表示一个词。
 * 因此“abc.#”能够匹配到“abc.def.ghi”,
 * 但是“abc.*” 只会匹配到“abc.def”
 * @Author: chenpt
 * @Description:
 * @Date: Created in 2022-07-28 15:28
 * @Modified By:
 */
@Configuration
public class TopicConfig {
    @Bean
    TopicExchange topicExchange(){
        return new TopicExchange(RabbitConst.topicExchange,true,false);
    }
    @Bean
    Queue topicQueue1(){
        return new Queue(RabbitConst.topicQueue1,true);
    }
    @Bean
    Queue topicQueue2(){
        return new Queue(RabbitConst.topicQueue2,true);
    }
    @Bean
    Binding bindingTopicQueue1(){
        return BindingBuilder.bind(topicQueue1()).to(topicExchange()).with(RabbitConst.topicRoutingKey1);
    }
    @Bean
    Binding bindingTopicQueue2(){
        return BindingBuilder.bind(topicQueue2()).to(topicExchange()).with(RabbitConst.topicRoutingKey2);
    }

}

controller

    @GetMapping("/sendTopic")
    @ApiOperation("topic队列发送")
    public Result sendTopicMessage(){
        rabbitTemplate.convertAndSend(RabbitConst.topicExchange,"chenpt.order.test","hello order test");
        rabbitTemplate.convertAndSend(RabbitConst.topicExchange,"chenpt.sms","hello sms test");
        return Result.success("ok");
    }

 consume

   @RabbitListener(queues = RabbitConst.topicQueue1)
    public void topicQueue1(String testMsg){
        System.out.println("topicReceiver1消费者收到消息  : " + testMsg);
    }

    @RabbitListener(queues = RabbitConst.topicQueue2)
    public void topicQueue2(String testMsg){
        System.out.println("topicReceiver2消费者收到消息  : " + testMsg);
    }

6:DeadExchange 死信交换机

当消息在队列中变成死信时,被重新发送到一个特殊的交换机中,同时绑定的队列就成为死信队列。

以下几种情况会导致消息变为死信:

消息被拒绝(Basic.Reject/Basic.Nack),并且设置requeue参数为false;
消息过期;
队列达到最大长度。

DeadConfig

此配置下声明了一个deadQueue,另外创建了一个测试队列和死信队列进行绑定

package com.person.chenpt.config.rabbit;

import com.person.chenpt.constans.RabbitConst;
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

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

/**
 * 死信队列--Dead-Letter-Exchange,死信交换器
 * 消息在一个队列中变成死信(Dead Letter)之后,被重新发送到一个特殊的交换器(DLX)中,同时,绑定DLX的队列就称为“死信队列”。
 * 以下几种情况导致消息变为死信:
 * 消息被拒绝(Basic.Reject/Basic.Nack),并且设置requeue参数为false;
 * 消息过期;
 * 队列达到最大长度。
 * @Author: chenpt
 * @Description:
 * @Date: Created in 2022-07-29 11:31
 * @Modified By:
 */
@Configuration
public class DeadConfig {
    @Bean
    FanoutExchange deadExchange(){
        return new FanoutExchange(RabbitConst.deadExchange,true,false);
    }
    @Bean
    Queue deadQueue(){
        return new Queue(RabbitConst.deadQueue,true);
    }
    @Bean
    Binding deadBinding(){
        return BindingBuilder.bind(deadQueue()).to(deadExchange());
    }
    @Bean
    DirectExchange directExchangeBindDead(){
        return new DirectExchange("testDirectEx",true,false);
    }
    @Bean
    Queue directQueueBindDead(){
        Map<String,Object> deadmap = new HashMap<>();
        // 绑定该队列到私信交换机
        deadmap.put("x-dead-letter-exchange",RabbitConst.deadExchange);
//        deadmap.put("x-dead-letter-routing-key",null);//由于deadExchange是fanout类型的所以不需要routing-key
        return new Queue("testDirectQue",true,false,false,deadmap);
    }

    @Bean
    Binding directBindDead(){
        return BindingBuilder.bind(directQueueBindDead()).to(directExchangeBindDead()).with("test");
    }
}

controller

    /**
     * 发送带有过期时间的消息 (10s)
     * 超时未消费的消息则进入死信队列
     * @return
     */
    @GetMapping("/sendDlx")
    @ApiOperation("dlx测试")
    public Result sendDlx(){
        rabbitTemplate.convertAndSend("testDirectEx","test","hello deadEx test",message -> {
            message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
            message.getMessageProperties().setExpiration("10000");
            return message;
        });
        return Result.success("ok");
    }

 consume和消息确认一起讲

开启手动确认--配置文件需要修改 

spring:
  rabbitmq: host: localhost port: 5672 username: admin password: admin virtual-host: / listener: #设置监听容器(Listener container)类型,如不设置,将会默认为SimpleRabbitListenerContainerFactory,且下面的direct手动确认配置不生效 type: direct direct: #开启手动确认 acknowledge-mode: manual #是否重试 retry: enabled: true

consume

  @RabbitListener(queues = "testDirectQue")
    public void testDirectQue(String testMsg, Message message, Channel channel) throws IOException {
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        try {
//            int err = 1/0;//模拟异常
            logger.info("testDirectQue消费者收到消息  : {},消费的主题消息来自  : {}",testMsg,message.getMessageProperties().getConsumerQueue());
            /**
             * 第二个参数,手动确认可以被批处理,当该参数为 true 时,则可以一次性确认 delivery_tag 小于等于传入值的所有消息
             */
            channel.basicAck(deliveryTag, true);
        } catch (Exception e) {
            logger.info("=====================消费者异常========================");
            /**
             * message.getMessageProperties().getRedelivered()
             * false 表示第一次进队列
             * true  表示重入队列
             */
            if (message.getMessageProperties().getRedelivered()) {
                logger.info("================消息已重复处理失败,拒绝再次接收======================" + testMsg);
                /**
                 * 拒绝消息,requeue=false 表示不再重新入队,如果配置了死信队列则进入死信队列、
                 * true会重新放回队列,所以需要自己根据业务逻辑判断什么时候使用拒绝
                 */
                channel.basicReject(message.getMessageProperties().getDeliveryTag(), false);
            } else {
                logger.info("====================消息即将再次返回队列处理=========================" + testMsg);
                /**
                 * requeue为是否重新回到队列,true重新入队
                 */
                channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
            }
        }
    }

 上述消息确认是在消费者中,其实生产者也有消息确认,当发送到交换机、队列时都有相应的回调函数代码如下

package com.person.chenpt.config.rabbit;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.ReturnedMessage;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 生产者
 * 消息确认
 * 可以在回调函数根据需求做对应的扩展或者业务数据处理
 * @Author: chenpt
 * @Description:
 * @Date: Created in 2022-07-28 16:00
 * @Modified By:
 */
@Configuration
public class RabbitProductConfirmConfig {

    @Bean
    public RabbitTemplate createRabbitTemplate(ConnectionFactory connectionFactory){
        RabbitTemplate rabbitTemplate = new RabbitTemplate();
        rabbitTemplate.setConnectionFactory(connectionFactory);
        //设置开启Mandatory,才能触发回调函数,无论消息推送结果怎么样都强制调用回调函数
        rabbitTemplate.setMandatory(true);
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            @Override
            public void confirm(CorrelationData correlationData, boolean ack, String cause) {
                System.out.println("ConfirmCallback:     "+"相关数据:"+correlationData);
                System.out.println("ConfirmCallback:     "+"确认情况:"+ack);
                System.out.println("ConfirmCallback:     "+"原因:"+cause);
            }
        });

        rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback() {
            @Override
            public void returnedMessage(ReturnedMessage returned) {
                System.out.println("ReturnCallback:     "+"消息:"+returned.getMessage());
                System.out.println("ReturnCallback:     "+"回应码:"+returned.getReplyCode());
                System.out.println("ReturnCallback:     "+"回应信息:"+returned.getReplyText());
                System.out.println("ReturnCallback:     "+"交换机:"+returned.getExchange());
                System.out.println("ReturnCallback:     "+"路由键:"+returned.getRoutingKey());
            }
        });

        return rabbitTemplate;
    }


} 

 

先记录这些基本使用应该没有问了,后续有知识点再更新记录 

  

  

  

 

  

 

  

  

  

  

 

 

posted @ 2022-08-03 15:30  不二尘  阅读(58)  评论(0编辑  收藏  举报