异步消息队列之RabbitMQ消息模型

RabbitMQ常用消息模型应用

RabbitMQ消息模型官方提供了如下七种,但是常用的也就前面五种:

  1. 简单队列模式----helloword
  2. 工作队列模式----work queue 在第一种模型(helloworld)之上,如果每一个消息在消费的时候如果处理时间过程,队列中消息越来越多,会导致消息不及时消费,造成很多问题,所以有了work
    queue(任务)的消息模型,这种消息模型就是多个消费者消费同一个队列里面的消息,消息不会被多次消费
  3. 广播模式----fanout 生产者把消息发送给交换机,在由交换机发送给各个订阅的队列(一个队列对应一个消费者),在由消费者消费,这种很消息模型还是比较常见的,比如说微信的订阅号,你订阅过后,就相当于队列订阅了交换机,然后生产者给交换机发送消息时,交换机就会给所有订阅他的队列发送消息
  4. 路由模式----Routing (路由)路由,就是可以按照路由key来匹配,确定要发送的队列,在有时候有些消息需要全部的消费者消费,有时候需要一部分消费者消费
  5. 主题模式----Topics (动态路由)在路由的消息模型之上,允许key使用通配符,这样就可以更加的灵活
  6. 远程过程调用模式----RPC
  7. 发布者确认模式----Publisher Confirms

其实那五种也很好区分,基本消息队列,工作消息队列,然后再根据发布订阅又分为三种广播、路由、主题。

提前声明:这里主要是各个模式代码的应用,每个模式都标注的很清楚,希望对你有帮助

先来说一下RabbitMQ中的几个角色,方便后续代码的理解:

  • publisher:生产者
  • consumer:消费者
  • exchange个:交换机,负责消息路由,注意:exchange只负责消息路由,而不是存储,路由失败则消息丢失
  • queue:队列,存储消息
  • virtualHost:虚拟主机,隔离不同用户的exchange、queue、消息的隔离

首先创建一个父工程mq-demo用来管理项目依赖,再创建两个模块,分别是publisher:消息的发送者,consumer:消息的消费者。构建如下所示:

在这里插入图片描述

导入父工程pom坐标

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>cn.nuo.demo</groupId>
    <artifactId>mq-demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>publisher</module>
        <module>consumer</module>
    </modules>
    <packaging>pom</packaging>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.9.RELEASE</version>
        <relativePath/>
    </parent>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!--AMQP依赖,包含RabbitMQ-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
         <!--JSON方式序列化-->
        <dependency>
            <groupId>com.fasterxml.jackson.dataformat</groupId>
            <artifactId>jackson-dataformat-xml</artifactId>
            <version>2.9.10</version>
        </dependency>
        <!--单元测试-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
    </dependencies>
</project>

在publisher和consumer服务中编写application.yml,添加mq连接信息内容都一样,来确定RabbitMQ地址【我是用Docker启动的RabbitMQ】:

logging:
  pattern:
    dateformat: MM-dd HH:mm:ss:SSS
spring:
  rabbitmq:
    host: 192.168.16.129 # 你自己的主机名
    port: 5672 # 端口
    virtual-host: / # 虚拟主机
    username: admin # 你自己的rabbitmq用户名
    password: 123 # 你自己的rabbitmq密码

在consumer服务中新建一个SpringRabbitListener类,编写消费逻辑:

package cn.nuo.mq.listener;

import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import java.time.LocalTime;
import java.util.Map;

@Component
public class SpringRabbitListener {
    // 1.helloworld模式
  /*  @RabbitListener(queues = "simple.queue")
    public void listenerSimpleQueue(String message){
        System.out.println("消费者接收到simple.queue队列的消息"+message);
    }*/

   /* 2.WorkQueue模型
   * Work queues,也被称为(Task queues)任务模型。当消息处理比较耗时的时候,可能生产消息的速度会远远大于消息的消费速度。
   * 长此以往,消息就会堆积越来越多,无法及时处理。此时就可以使用work 模型:让多个消费者绑定到一个队列,共同消费队列中的消息。
   * 队列中的消息一旦消费,就会消失,因此任务是不会被重复执行的。
   * 消费者1,领取任务并且完成任务,假设完成速度较慢
     消费者2:领取任务并完成任务,假设完成速度快
*/
    @RabbitListener(queues = "simple.queue")
    public void listenerWorkQueue1(String message) throws InterruptedException {
        System.out.println("消费者1接收到的消息"+message+ LocalTime.now());
        Thread.sleep(20);
    }
    @RabbitListener(queues = "simple.queue")
    public void listenerWorkQueue2(String message) throws InterruptedException {
        System.err.println("消费者2----接收到的消息"+message+LocalTime.now());
        Thread.sleep(200);
    }
    //3.FanoutExchange模式会将接收到的消息广播到每一个跟其绑定的queue
    @RabbitListener(queues = "fanout.queue1")
    public void listenerFanoutExchange1(String message)  {
        System.out.println("消费者1接收到的消息"+message);
    }
    @RabbitListener(queues = "fanout.queue2")
    public void listenerFanoutExchange2(String message)  {
        System.err.println("消费者2----接收到的消息"+message);
    }
    
    /*描述下Direct交换机与Fanout交换机的差异?
Fanout交换机将消息路由给每一个与之绑定的队列
Direct交换机根据RoutingKey判断路由给哪个队列
如果多个队列具有相同的RoutingKey,则与Fanout功能类似*/

    //4.路由模式-DirectExchange,会将接收到的消息根据规则路由到指定的Queue,
    @RabbitListener(bindings = @QueueBinding(value =@Queue(name = "direct.queue1"),
            exchange =@Exchange(name = "itcast.direct",type = ExchangeTypes.DIRECT),
            key ={"red","blue"}))
    public void listenerDirectExchange1(String message)  {
        System.out.println("消费者1接收到的消息"+message);
    }

    @RabbitListener(bindings = @QueueBinding(value = @Queue(name = "direct.queue2"),
            exchange = @Exchange(name = "itcast.direct",type = ExchangeTypes.DIRECT),
            key = {"red","pink"}))
    public void listenerDirectExchange2(String message) {
        System.err.println("消费者2----接收到的消息"+message);
    }

    /*   TopicExchange与DirectExchange类似,区别在于Topic交换机接收的消息RoutingKey必须是多个单词,并且以 . 分割。
		Queue与Exchange指定BindingKey时可以使用通配符:
		#:代指0个或多个单词
		*:代指一个单词
    */
    @RabbitListener(bindings = @QueueBinding(value = @Queue(name = "topic.queue1"),
            exchange = @Exchange(name = "itcast.topic",type = ExchangeTypes.TOPIC),
            key = {"china.#"}))
    public void listenerTopicExchange1(String message){
        System.err.println("消费者1----接收到的消息"+message);
    }
    @RabbitListener(bindings = @QueueBinding(value = @Queue(name = "topic.queue2"),
    exchange = @Exchange(name = "itcast.topic",type = ExchangeTypes.TOPIC),
    key = {"china.*"}))
    public void listenerTopicExchange2(String message){ //
        System.err.println("消费者2----接收到的消息"+message);
    }

    //6.消息转换器
    @RabbitListener(queues = "object.queue")
    public void listenerObjectQueue(Map<String,Object> message){
        System.err.println("消费者----接收到的消息"+message);
    }

}



在consumer服务中新建FanoutConfig类

package cn.nuo.mq.config;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
//FanoutExchange模式
@Configuration
public class FanoutConfig {
/*- 创建一个交换机 itcast.fanout,类型是Fanout
-创建两个队列fanout.queue1和fanout.queue2,
绑定到交换机itcast.fanout*/

    //声明FanoutExchange交换机
    @Bean
    public FanoutExchange fanoutExchange(){
        return new FanoutExchange("itcast.fanout");
    }
    //声明一个队列
    @Bean
    public Queue fanoutQueue1(){
        return new Queue("fanout.queue1");
    }
    //将队列1绑定到交换机
    @Bean
    public Binding bindQueue1(Queue fanoutQueue1,FanoutExchange fanoutExchange){
        return BindingBuilder.bind(fanoutQueue1).to(fanoutExchange);
    }

    //声明第二个队列
    @Bean
    public Queue fanoutQueue2(){
        return new Queue("fanout.queue2");
    }
    //将队列2绑定到交换机
    @Bean
    public Binding bindQueue2(Queue fanoutQueue2,FanoutExchange fanoutExchange){
        return BindingBuilder.bind(fanoutQueue2).to(fanoutExchange);
    }
    
    
    //这是消息转换器的,与广播无关
    @Bean
    public Queue objectMessageQueue(){
            return new Queue("object.queue");
    }
    @Bean
    public MessageConverter jsonMessageConverter(){
        return new Jackson2JsonMessageConverter();
    }
}

在publisher服务创建SpringAmqpTest类用来发送消息的测试

package cn.nuo.mq.spring;


import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.HashMap;
import java.util.Map;

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringAmqpTest {
    @Autowired
    private RabbitTemplate rabbitTemplate;
    
    //简单队列模式----helloword
    @Test
    public void testAmqp(){
        String queueName = "simple.queue";
        String message = "hello, SpringAmqp!";
        rabbitTemplate.convertAndSend(queueName,message);
    }

    //WorkQueue模型
    @Test
    public void testWorkQueue() throws InterruptedException {
        String queueName = "simple.queue";
        String message = "hello, WorkQueue!";
        for (int i = 0; i < 50; i++) {
            rabbitTemplate.convertAndSend(queueName,message+i);
            Thread.sleep(20);
        }
    }
    
    //FanoutExchange模式
    @Test
    public void testFanoutExchange(){
        String exchangeName = "itcast.fanout";
        String message = "hello, FanoutExchange!";
        // 发送消息,参数分别是:交互机名称、RoutingKey(暂时为空)、消息
        rabbitTemplate.convertAndSend(exchangeName,"",message);
    }
    
    //路由模式,发布订阅-DirectExchange
    @Test
    public void testDirectExchange(){
        String exchangeName = "itcast.direct";
        String message = "hello,DirectExchange";
        rabbitTemplate.convertAndSend(exchangeName,"red",message);
    }
    
    //发布订阅-TopicExchange
    @Test
    public void testTopicExchange(){
        String exchangeName = "itcast.topic";
        String message = "hello,TopicExchange";
        rabbitTemplate.convertAndSend(exchangeName,"china.news",message);
    }
    
    //消息转换器
    @Test
    public void testSendMap(){
        String queueName = "object.queue";
        Map<String,Object> msg = new HashMap<>();
        msg.put("name","jack");
        msg.put("age",18);
        rabbitTemplate.convertAndSend("object.queue",msg);
    }

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值