RabbitMQ基础(未完待续)

52 阅读6分钟

RabbitMQ消息模型的核心组件

  1. 消息生产者(Producer)
  • 定义:生产者是负责生成并发送消息的应用程序。
  • 功能:生产者将消息发送到交换器,交换器再根据特定的路由规则将消息分发到队列。
  1. 消息消费者(Consumer)
  • 定义:消费者是接收并处理消息的应用程序。
  • 功能:消费者从队列中获取消息并执行相应的业务逻辑,处理完毕后可以确认消息的消费。
  1. 消息(Message)
  • 定义:消息是生产者发送的一个包含数据的包。
  • 组成:消息主要由两部分构成:
    • 消息体(Body):包含实际的数据内容。
    • 消息头(Header):包含元数据,如路由键、消息属性等。
  1. 队列(Queue)
  • 定义:队列是一个用于存储消息的缓冲区,消费者从中读取消息进行处理。
  • 功能:队列确保消息按发送顺序存储,并提供可靠的消息持久化机制。
  1. 交换器(Exchange)
  • 定义:交换器接收来自生产者的消息,并根据绑定和路由键将消息路由到一个或多个队列。
  1. 路由键(Routing Key)
  • 定义:路由键是一个字符串,用于交换机将消息路由到特定队列。
  • 功能:通过路由键,生产者可以指定消息的目标队列。
  1. 绑定(Binding)
  • 定义:绑定是交换机和队列之间的链接,定义了消息如何从交换机到达队列。
  • 功能:绑定包含路由规则,决定消息路由路径。

RabbitMQ消息模型

  1. 基本消息队列(BasicQueue) yuque_diagram.png

    工作流程

    • 生产者将消息发送到队列。
    • 消费者从队列中读取消息并处理。
    • 消息在被消费后从队列中移除。

    代码示例

    注意在实现这些功能前需要导入SpringAMQP依赖

    生产者代码

    @SpringBootTest
    public class SpringAmqpTest{
        //自动装配RabbitTemplate模板对象
        @Autowired
        private RabbitTemplate rabbitTemplate;
        /**
        * Basic Queue简单队列模型
        */
        @Test
        public void testSimpleQueue(){
            //1.定义变量保存队列名
            String queueName = "simple.queue";
            //2.定义变量保存发送的消息
            String message = "hello,world!";
            //3.发送消息
            rabbitTemplate.convertAndSend(queueName, message);
        }
    }
    

    消费者代码

    @Component
    public class SpringRabbitListener{
        //定义监听简单消息队列simple.queue的消息
        /* 1.RabbitListener 注解中的属性:String[] queues() default{}; 书写监听哪个队列的名字
        */
        @RabbitListener(queues = "simple.queue")
        public void listenSimpleMessage(String msg){
            System.out.println("yes, I know" + msg);
        }
    }
    
  2. 工作队列模式(WorkQueue) yuque_diagram.png

    工作流程

    • 生产者将任务消息发送到队列。
    • 多个消费者监听同一个队列,分摊处理任务。
    • 任务在被消费后从队列中移除。

    代码示例

    生产者代码

    /*
    * workQueue
    * 向队列中不停发送消息,模拟消息堆积。
    */
    @Test
    @RabbitListener(queuesToDeclare = @Queue("work.queue"))
    public void testWorkQueue() throws InterruptedException{
        // 1.定义变量保存存放消息的队列名
        String queueName = "work.queue";
        //2.定义变量保存发送的消息
        String message = "hello, world";
        //3.发送消息
        for(int i = 1; i <= 60; i++){
            rabbitTemplate.convertAndSend(queueName, message + i);
            //每隔20毫秒发送
            Thread.sleep(20);
        }
    }
    

    消费者代码

    //定义消费者1消费工作队列的消息
    @RabbitListener(queues = "work.queue")
    public void listenWork1Message(String msg) throws InterruptedException{
        System.out.println("消费者1接收到" + msg);
        //休眠20ms
        Thread.sleep(20);
    }
     //定义消费者2消费工作队列的消息
    @RabbitListener(queues = "work.queue")
    public void listenWork2Message(String msg) throws InterruptedException{
        System.out.println("消费者2接收到" + msg);
        //休眠20ms
        Thread.sleep(20);
    }
    
  3. 发布订阅(Publish、Subscribe),又根据交换机类型不同分为三种:

  • 广播

yuque_diagram.png

工作流程

  • 生产者将消息发送到交换器。

  • 多个队列订阅交换器,接收消息。

  • 每个队列的消费者从队列中获取消息并处理。

    代码示例

    生产者代码

    /** 
    * Fanout Exchange:广播
    */
    @Test
    public void testFanoutExchange(){
        //1.定义变量保存交换机的名字
        String exchangeName="change.fanout";
        //2.定义变量保存发送的消息
        String message="hello,world!";
        //3.发送消息
        //第二个参数routingkey,这里实现的是广播交换机,不需要routingkey,因此这里是空字符串
        rabbitTemplate.convertAndSend(exchangeName,"",message);
    }
    
    

    消费者代码

    配置类实现方式:

    Config

    /*
    * @Configuration 将修饰的类FanoutConfig对象放到SpringIOC容器中
    */
    @Configuration
    public class FanoutConfig{
        //1.定义方法生命交换机,将交换机对象放到SpringIOC容器中
        //@Bean修饰的方法返回值对象作为SpringIOC容器中的value,方法名作为key
        @Bean
        public FanoutExchange fanoutExchange(){
            //change.fanout表示交换器的名字
            return new FanoutExchange("change.fanout");
        }
        //2.定义方法声明队列1
        @Bean
        public Queue fanoutQueue1(){
            //fanout.queue1 表示队列名
            return new Queue("fanout.queue1");
        }
        //3.定义方法将队列1绑定到交换机change.fanout上
        //下面方法的形参FanoutExchange fanoutExchange的名字和上述 public FanoutExchange fanoutExchange(){}方法名一致
        @Bean
        public Bingding bingingQueue1ToExchange(FanoutExchange fanoutExchange, Queue fanoutQueue1){
        /*
            public Binding to(FanoutExchange exchange){}
        */
        return BindingBuilder.bind(fanoutQueue1).to(fanoutExchange);
        }
        //4.定义方法声明队列2
        @Bean
        public Queue fanoutQueue2(){
            //fanout.queue2 表示队列名
            return new Queue("fanout.queue2");
        }
    
        //5.定义方法将队列2绑定到交换机上
        @Bean
        public Binding bindingQueue2ToExchange(FanoutExchange fanoutExchange,Queue fanoutQueue2){
            /*
                public Binding to(FanoutExchange exchange){}
             */
            return BindingBuilder.bind(fanoutQueue2).to(fanoutExchange);
        }
    }
    

    消费者代码

    @RabbitListener(queues = "fanout.queue1")
    public void listenFanout1Message(String msg){
        System.out.println("消费者1接收到了生产者发送的消息:"+msg);
    }
    
    @RabbitListener(queues = "fanout.queue2")
    public void listenFanout2Message(String msg){
        System.out.println("消费者2接收到了生产者发送的消息:"+msg);
    }
    
  • 路由交换机实现

yuque_diagram.png

工作流程

  • 生产者将消息发送到交换器,指定路由键。
  • 队列通过绑定路由键订阅交换器。
  • 交换器根据路由键将消息路由到相应的队列。

代码示例

生产者代码

/**
* Direct Exchange:路由
*/
@Test
public void testSendDirectExchange(){
    //1.定义变量保存交换机名
    String exchangeName = "exchange.direct";
    //2.定义变量保存消息
    String message="hello,world!";
    //3.发送消息 "one" 路由key
    rabbitTemplate.convertAndSend(exchange, "one", message);
}

消费者代码

/*
*队列1
*1.@RabbitListener 注解的属性:QueueBinding[] bindings() default{};
说明:QueueBingding也是一个注解,表示队列绑定,当前队列和哪个交换机绑定
2.@QueueBinding注解中常见属性
    1)Queue value(); 属于Queue,Queue是一个注解,在Queue注解中有一个属性name,name属性值表示书写的队列名
      value=@Queue(name="direct.queue1") ===>direct.queue1 表示队列名
    2)Exchange exchange();属于Exchange类型,是一个注解,在Exchange注解中有属性
        I:String name() default ""; 表示交换机的名字 name="change.direct"==》change.direct是交换机名字
        II:String type() default "direct" 表示交换机类型===> ExchangeTypes.DIRECT就是"direct"
    3)String[] key() default{}; 表示绑定的routing key ===> key={"one", "two"}

*/
@RabbitListener(bindings = {@QueueBinding(value=@Queue(name="direct.queue1"),
exchange = @Exchange(name="exchange.direct", type = ExchangeTypes.DIRECT), key={"one", "two"})})
public  void listenDirect1Message(String msg){
    System.out.println("消费者1收到" + msg);
}
/*
*队列2
*/
@RabbitListener(bindings = {@QueueBinding(value=@Queue(name="direct.queue2"),
exchange = @Exchange(name="exchange.direct", type = ExchangeTypes.DIRECT), key={"two", "three"})})
public  void listenDirect1Message(String msg){
    System.out.println("消费者2收到" + msg);
}

  • 主题交换机实现

yuque_diagram.png

工作流程

  • 生产者将消息发送到交换器,指定模式匹配的路由键。
  • 队列通过绑定模式匹配的路由键订阅交换器。
  • 交换器根据模式匹配的路由键将消息路由到相应的队列。

代码示例

生产者代码

/**
 * Topic Exchange:主题
 */
@Test
public void testSendTopicExchange() {
    //1.定义变量保存交换机名
    String exchangeName = "exchange.topic";
    //2.定义变量保存消息
    String message="hello world";
    //3.发送消息 one.message 表示routing key
    rabbitTemplate.convertAndSend(exchangeName,"one.message",message);
}

消费者代码

 /*
    定义方法模拟队列1接收主题topic交换机的消息
     key={"one.#"} 表示routing key 这里#表示大于等于0个字符
*/
@RabbitListener(bindings = @QueueBinding(
                                            value=@Queue(name="topic.queue1"),
                                            exchange = @Exchange(name="exchange.topic",type= ExchangeTypes.TOPIC),
                                            key={"one.#"}
))
public void listenTopic1Message(String msg){
    System.out.println("消费者1接收"+msg);
}
/*
    TODO: key={"#.one"} #表示大于等于个字符,发送者发送的routingkey只要以one结尾就会将消息路由到当前队列
 */
@RabbitListener(bindings = @QueueBinding(
        value=@Queue(name="topic.queue2"),
        exchange = @Exchange(name="itcast.topic",type= ExchangeTypes.TOPIC),
        key={"#.news"}
))
public void listenTopic2Message(String msg){
    System.out.println("消费者2接收到了生产者发送的消息:"+msg);
}