RabbitMQ

MQ

消息队列 是指利用 高效可靠消息传递机制 进行与平台无关的 数据交流,并基于 数据通信 来进行分布式系统的集成

存放消息的队列,是一种跨进程的通信机制,用于上下游传递消息。

MQ三大应用

削峰

比如说下订单系统最多能处理一万次订单,但是高峰期超出,要限制超出的部分,就使用消息队列做缓冲,把一秒内的订单分散一段时间处理

在这里插入图片描述

异步

一个下单操作,业务流程很长,响应时间长,用户体验不好。

使用消息队列,下单时只用调用支付系统,其他业务可以通过消息方式发出,其他系统接收到消息去执行,实现异步处理
在这里插入图片描述

解耦

电商系统有很多子系统,下单系统、库存系统、物流系统、支付系统,用户下单,只要有一个系统出现故障,都会影响其他。

使用消息队列,一个系统出故障后,请求可以暂放到消息队列中,等修复后再处理,就不影响其他操作。

缺点

  • 系统可用性降低:消息队列万一挂了就g了
  • 系统复杂性增加:一致性问题、如何保证消息不被重复消费,如何保证消息可靠传输

JMS & AMQP

JMS:定义了对消息操作的统一接口,限定必须使用Java语言;只有队列模式和主题模式两种模式;ActiveMQ和RocketMQ都基于JMS

AMQP:高级消息对列协议,规定的是数据格式,所以不限制语言;多种模式。RabbitMQ

MQ分类

在这里插入图片描述

  • Kafka:适应产生大量数据收集业务,日志采集
  • RocketMQ:金融互联网,扣订单
  • RabbitMQ:时效性,延迟低

RabbitMQ

接收、存储、转发的中间件

组成

在这里插入图片描述

在这里插入图片描述

  • Broker:接收和分发消息的应用,RabbitMQ Server
  • Connection:发送消息和接收消息的TCP长连接
  • Channel:信道,发消息的通道,一个信道可以发送多个消息,轻量级的连接减少建立TCP连接开销
  • Exchange:交换机
  • Binging:交换机与队列的虚拟连接
  • Queue:消息会最终送到这,被消费者取走

安装

安装链接: Linux下安装RabbitMQ

看着这个教程一下就能成功

启动:/usr/local/software/rabbitmq_software/rabbitmq_server-3.7.16/sbin/rabbitmq-server -detached

浏览器访问:http://192.168.32.129:15672/
在这里插入图片描述
添加依赖

<!--rabbitmq依赖客户端-->
        <dependency>
            <groupId>com.rabbitmq</groupId>
            <artifactId>amqp-client</artifactId>
            <version>5.8.0</version>
        </dependency>

        <!--操作文件流的依赖-->
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.6</version>
        </dependency>

模式总汇

  1. hello word 模式:一个消费者对应一个队列对应一个消费者
  2. 工作队列模式:队列中的消息可以被多个消费者消费,但消息只能被消费一次;用于提高消费消息的速度
  3. 广播模式:消息发送到交换机,交换机与队列建立绑定关系完成消息分发;消息可以被共同消费
  4. 路由模式:绑定时需要指定routing key,发送消息也要携带routing key,就可以进行类似筛选发送
  5. 主题模式:routing key支持通配符,更加灵活

Hello World

在这里插入图片描述

发送
/**
 * 生产者:发消息
 *
 * @author Deevan
 */
public class Producer {

    //队列名称
    public static final String QUEEN_NAME = "hello";

    //发消息
    public static void main(String[] args) throws IOException, TimeoutException {
        //创建一个连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //设置连接相关信息 (主机、用户名、密码)
        factory.setHost("192.168.32.129");
        factory.setUsername("admin");
        factory.setPassword("000");
        //通过工厂创建连接
        Connection connection = factory.newConnection();
        //通过连接创建信道
        Channel channel = connection.createChannel();
        /*
            生成一个队列
                1.队列名称
                2.队列里的消息是否持久化(磁盘),默认情况存储在内存中
                3.该队列是否直供一个消费者进行消费 是否进行消费共享:true可以多个  false只有一个
                4.是否自动删除 最后一个消费者断开连接后  该队是否自动删除  true自动删除
                5.其他参数 null
         */
        channel.queueDeclare(QUEEN_NAME, false, false, false, null);
        //编辑消息
        String message = "Hello World";
        /*
            发送一个消息
                1.发送到哪个交换机
                2.路由的Key值是哪个  本次是队列的名称
                3.其他消息参数
                4.发送的消息体
         */
        channel.basicPublish("", QUEEN_NAME, null, message.getBytes());
        System.out.println("消息发送成功");
    }
}

有一个消息正在准备被消费,总共一个消息
在这里插入图片描述
在这里插入图片描述

接收
/**
 * 消费者:接收消息
 *
 * @author Deevan
 */
public class Consumer {

    //队列名称
    public static final String QUEUE_NAME = "hello";

    //接收消息
    public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("192.168.32.129");
        factory.setUsername("admin");
        factory.setPassword("000");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();

        //声明接收消息(3)
        DeliverCallback deliverCallback = (var1, var2) -> {
            System.out.println(new String(var2.getBody()));
        };
        //取消消息时回调(4)
        CancelCallback cancelCallback = (var1) -> {
            System.out.println("消息被中断");
        };

        /*
            消费者消费消息
                1.消费哪个队列
                2.消费之后是否要自动答应  true自动 false手动
                3.消费者未成功消费的回调
                4.消费者取消消费的回调
         */
        channel.basicConsume(QUEUE_NAME, true, deliverCallback, cancelCallback);
    }
}

消息队列弹出消息,消费者接收消息
在这里插入图片描述

Work Queues

工作队列(任务队列):大量任务到来,避免立即执行资源密集型任务---->把任务封装为消息并发送到队列,后台多个进程将任务弹出并已轮询的方式执行

在这里插入图片描述

轮询接收消息


/**
 * 消息生产者,从控制台输入
 *
 * @author Deevan
 */
public class Task {

    public static final String QUEUE_NAME = "hello";

    public static void main(String[] args) throws IOException, TimeoutException {
        Channel channel = RabbitMqUtil.getChannel();
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNext()) {
            String message = scanner.next();
            channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
            System.out.println("发送消息完成:\t" + message);
        }
    }
}
/**
 * 消费者接收消息,多个消费者采用轮询的方式接收
 *
 * @author Deevan
 */
public class Worker {
    public static final String QUEUE_NAME = "hello";

    public static void main(String[] args) throws IOException, TimeoutException {
        Channel channel = RabbitMqUtil.getChannel();
        //消息接收回调
        DeliverCallback deliverCallback = (var1, var2) -> {
            System.out.println("接收到的消息:" + new String(var2.getBody()));
        };
        //消息取消时回调
        CancelCallback cancelCallback = (var1) -> {
            System.out.println(var1 + "消费者取消消息回调");
        };
        channel.basicConsume(QUEUE_NAME, true, deliverCallback, cancelCallback);
    }
}

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

消息应答

问题:MQ向消费者发送消息,消费者挂掉了,但是MQ不知道,MQ会删除发出的消息,就会造成消息丢失

消息应答就是在消费者处理消息后,告诉MQ它已经处理了,MQ就可以把该消息删除了

自动应答

接收到消息就会应答

手动应答

可以批量应答multiple)减少网络开销,保证消息不丢失

  1. Channel.basicAck():用于肯定确认,通知MQ可以丢弃
  2. Channel.basicNack():用于否定确认
  3. Channel.basicReject():用于否定确认,少个参数(是否批量处理)

在这里插入图片描述
在这里插入图片描述

消息自动重新入队

消息没有被执行完没有响应Ack,MQ知道了就把该消息重新分配给其他消费者;保证消息不丢失
在这里插入图片描述

更改消费者代码

在这里插入图片描述
一个消费者挂掉了,他未完成接收的消息会被其他消费者执行,不会丢失

Rabbit持久化

在RabbitMQ挂掉之后仍能保证消息不丢失

队列消息都应持久化

队列持久化

即使MQ宕机队列仍存在
在这里插入图片描述
在这里插入图片描述

消息持久化

将消息保存到磁盘,也不是绝对不丢失(绝对要:发布确认)
在这里插入图片描述

不公平分发

轮询分发公平
不公平分发:按能力分发
在这里插入图片描述

预取值

指定分发
在这里插入图片描述
在这里插入图片描述

发布确认

队列、消息持久化要保存到磁盘上,也可能会出现消息丢失

生产者产出消息,MQ将消息保存到磁盘上 告诉生产者进行确认,即可解决

开启发布确认
channel.confirmSelect();
单个确认

发布速度很慢,出问题知道问题在哪

//单个确认
boolean flag = channel.waitForConfirms();
批量确认

等待100条消息一次确认
batchSize = 100;
在这里插入图片描述

异步批量确认

无论MQ是否收到消息,都会给发送者一个回调

  1. 使用一个线程安全有序的map记录发出的消息
  2. 确认的消息在确认的消息回调中进行删除(map中该消息)
  3. 剩余的就是未确认的消息,在未确认的消息回调中处理

性能最好,可准确确认到哪个消息确认失败并能作出处理
在这里插入图片描述

/**
 * 异步确认应答
 *
 * @author Deevan
 */
public class Task {
    public static void main(String[] args) throws IOException, TimeoutException {
        Channel channel = RabbitMqUtil.getChannel();
        String queueName = UUID.randomUUID().toString();
        channel.queueDeclare(queueName, true, false, false, null);
        //开启发布确认
        channel.confirmSelect();
        long s = System.currentTimeMillis();

        //线程安全的有序哈希表:1.将序号和消息关联 2.给到序号可以批量删除消息 3.线程安全
        ConcurrentSkipListMap<Long, String> outstandingConfirms = new ConcurrentSkipListMap<>();

        //准备消息监听器  监听那些消息发送成功 那个消息失败
        //消息成功回调
        ConfirmCallback ackCallback = (var1, var3) -> {
            //批量确认
            if (var3) {
                //删除map中确认的消息,剩下的就是未确认的
                ConcurrentNavigableMap<Long, String> confirmed = outstandingConfirms.headMap(var1);
                confirmed.clear();
            } else {
                //单个确认
                outstandingConfirms.remove(var1);
            }
        };
        //消息失败回调   1.消息标记   2.是否批量确认
        ConfirmCallback nackCallback = (var1, var3) -> {
            //接收未确认消息和消息体
            String nackMessage = outstandingConfirms.get(var1);
            System.out.println("未确认的消息标记:" + var1 + "\t该消息内容是:" + nackMessage);
        };
        //异步通知    1.监听那些消息成功 2.那些消息失败
        channel.addConfirmListener(ackCallback, nackCallback);

        //1000个消息
        for (int i = 0; i < 1000; i++) {
            String message = i + "";
            channel.basicPublish("", queueName, null, message.getBytes());
            //记录发送的所有消息,放在map中
            outstandingConfirms.put(channel.getNextPublishSeqNo(), message);
        }
        long e = System.currentTimeMillis();
        System.out.println("耗时:" + (e - s) + "ms");
    }
}
三种方式对比

在这里插入图片描述

交换机

  1. 接收来自生产者的消息
  2. 将消息推送到队列

交换机可以确定这些消息是以哪种方式推送到队列

类型

  1. 直接(direct)路由类型
  2. 主题(topic)
  3. 标题(headers)
  4. 扇出(fanout)发布订阅
  5. 无名类型:“ ”表示,默认

临时队列在这里插入图片描述
绑定

交换机与队列的捆绑关系

广播模式(扇出交换机)

简单模式/工作模式:生产的消息只能被消费一次
在这里插入图片描述

队列中的一个消息仍只能被消费一次,单使用交换机绑定队列,生产者发送的一个消息可以被多个消费

交换机绑定队列,生产者发消息向交换机,交换机根据模式确定发送给哪些队列;同时多个消费者可以获取这些队列中的消息
在这里插入图片描述
一个交换机logs绑定了两个队列
在这里插入图片描述

生产者发送消息(发布)

/**
 * 发送者
 *
 * @author Deevan
 */
public class EmitLog {
    public static final String EXCHANGE_NAME = "logs";

    public static void main(String[] args) throws IOException, TimeoutException {
        Channel channel = RabbitMqUtil.getChannel();
        //channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNext()) {
            String message = scanner.next();
            channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes());
            System.out.println("生产者发出消息:" + message);
        }
    }
}

多个消费者(订阅)

/**
 * 接受者接收消息(其中之一)
 *
 * @author Deevan
 */
public class ReceiveLogs01 {
    public static final String EXCHANGE_NAME = "logs";

    public static void main(String[] args) throws IOException, TimeoutException {
        Channel channel = RabbitMqUtil.getChannel();
        //声明交换机    名称      类型(发布订阅)
        channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
        //声明临时队列:名称随机,消费者断开连接队列自动删除
        String queueName = channel.queueDeclare().getQueue();
        //绑定交换机与队列
        channel.queueBind(queueName, EXCHANGE_NAME, "");
        System.out.println("01等待接收消息");

        //接收消息回调
        DeliverCallback deliverCallback = (var1, var2) -> {
            System.out.println("01接收到的消息" + new String(var2.getBody()));
        };
        //接收消息
        channel.basicConsume(queueName, true, deliverCallback, cancel -> {});
    }
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

路由模式(直接交换机)

与扇出交换机的区别就是多重绑定routingKey,交换机根据routingKey决定发送给谁
在这里插入图片描述

主题模式(主题交换机)

利用类似正则表达式的模式(#,*)绑定router_key,更加灵活
可包含 扇出交换机和直接交换机
在这里插入图片描述
接受者
在这里插入图片描述
发送者
在这里插入图片描述

消息传输保证层级

At most once:最多一次。消息可能会丢失,但不会重复传输。

At least once:最少一次。消息绝不会丢失,但可能会重复传输。

Exactly once: 恰好一次,每条消息肯定仅传输一次

队列结构

通常由以下两部分组成:
rabbit_amqqueue_process:负责协议相关的消息处理,即接收生产者发布的消息、向消费者交付消息、处理消息的确认(包括生产端的 confirm 和消费端的 ack) 等。
backing_queue:是消息存储的具体形式和引擎,并向 rabbit amqqueue process 提供相关的接口以供调用。

死信队列

无法被消费消息

为保证业务消息数据不丢失,当消息发生异常时,将消息发送到死信队列。比如用户下单后超过指定时间不支付时订单自动失效

死信的三大产生

  1. 消息TTL过期
  2. 队列满了
  3. 消息被拒绝(basic.nack且不重新放到队列中)

在这里插入图片描述

延时队列

存放延迟消息的队列,延迟消息就是当消息被消费者发送后,并不想让消费者立即拿到消息,而是等待指定的时间,才让消费者消费。

RabbitMQ的延时队列需要通过设置过期时间和死信队列模拟出来。将消息加入队列并设置过期时间,时间过后就会放入死信队列,通过监听死信队列。

可以用两种方式设置消息过期时间:①设置队列属性,队列中的消息都有相同的过期时间;②设置每个消息的过期时间。

使用场景

  1. 订单在指定时间未支付自动销毁
  2. 新创建的店铺在十天内未上传商品,自动发消息提醒
  3. 用户注册成功后,三天没有登录就发短信提醒
  4. 预定会议后,在预定时间前十分钟通知大家参会

如何保证消息可靠传输

也就是防止消息丢失,可以分三个方面:生产者端、MQ本身、消费者端

生产者端

发送的消息MQ没接收到,生产者却不知道,误以为消息发送成功,导致消息丢失。在生产者端channel.confirmSelect()开启发布确认confirm模式后,在信道设置一个回调监听函数channel.addConfirmListener(ackCallback, nackCallback),每个消息都会分配一个唯一的id, 这样对于每个消息,MQ有没有接收到都会告知生产者,发送失败的消息就可以重发,保证消息不丢失。有(单个、批量、异步批量)三种模式可选。这个发布确认机制是异步的,不会使发送消息阻塞。

MQ自身

使用MQ持久化,也就是将交换机、消息、队列持久化到磁盘上,这样MQ停机了也可以恢复,防止消息丢失。

交换机持久化:在生成交换机时可以通过构造方法设置交换机持久化durable和是否自动删除;
队列持久化:也是在生成对列是通过durable参数设置
消息持久化:这个不用配置;因为底层MessageProperties的持久化策略默认是MessageDeliveryMode.PERSISTENT,初始化时默认消息时持久化的。

消费者端

MQ向消费者发送消息,消费者挂掉了,但是MQ不知道,MQ会删除发出的消息,就会造成消息丢失。

消息应答就是在消费者处理消息后,告诉MQ它已经处理了,MQ就可以把该消息删除了

RabbitMQ默认使用自动应答,也就是接收到消息就会应答,如果下面出错,该消息仍被MQ删除,所以要先关闭自动应答,开启手动应答。

也就是在程序处理完成后再对MQ应答,MQ超过指定时间没有接收到这个应答或者接收到否定应答,就会让该消息重新入队。

但要防止消息重复消费

如何防止重复消费

生产者端

生产者发送消息给MQ,在MQ确认的时候出现了网络波动,生产者没有收到确认,实际上MQ已经接收到了消息。这时候生产者就会重新发送一遍这条消息。

生产者中如果消息未被确认,或确认失败,我们可以使用定时任务+(redis/db)来进行消息重试

消费者端

消息发送到消费者后,消费者确认的应答由于网络没有到MQ,MQ误认为消费者没有成功消费,将消息重新分发,造成消息重复消费。

需要保证消费者幂等性

  1. 数据库修改操作、redis的set操作 天然幂等
  2. 数据库全局唯一id
  3. 给消息加上全局唯一id,消费了就放到redis里,操作时看redis有没有

消息积压

大量消息积压在MQ的一个队列中,消费者需要很长时间才能完成消费

需要完成临时紧急扩容 队列 和 消费者

  1. 停掉现有的消费者,新建多个队列
  2. 写一个临时分发消息、不处理业务的程序,接收原有队列中的消息,直接轮询分发到新建的多个对列中
  3. 新建同等数量的消费者去处理新建队列中的请求

保证消息有序性

比如说一个生产者向对列中按顺序发送ABC三个数据,这个队列由三个消费者轮询消费,结果存入数据库顺序就不一定是ABC
在这里插入图片描述
拆分多个 queue,每个 queue 一个 consumer,就是多一些 queue 而已,确实是麻烦点;或者就一个 queue 但是对应一个 consumer,然后这个 consumer 内部用内存队列做排队,然后分发给底层不同的 worker 来处理
在这里插入图片描述

事务机制

RabbitMQ 客户端中与事务机制相关的方法有三个:

channel.txSelect 用于将当前的信道设置成事务模式。

channel . txCommit 用于提交事务 。

channel . txRollback 用于事务回滚,如果在事务提交执行之前由于 RabbitMQ 异常崩溃或者其他原因抛出异常,通过txRollback来回滚

MQ集群

RabbitMQ 有三种模式:单机模式,普通集群模式,镜像集群模式。

单机模式:就是demo级别的,一般就是你本地启动了玩玩儿的,没人生产用单机模式

普通集群模式:意思就是在多台机器上启动多个RabbitMQ实例,每个机器启动一个。

镜像集群模式:这种模式,才是所谓的RabbitMQ的高可用模式,跟普通集群模式不一样的是,你创建的queue,无论元数据(元数据指RabbitMQ的配置数据)还是queue里的消息都会存在于多个实例上,然后每次你写消息到queue的时候,都会自动把消息到多个实例的queue里进行消息同步。

SpringAMQP

spring-amqp是基础抽象
spring-rabbit是RabbitMQ的实现
在这里插入图片描述

  • 用于发布和接收消息RabbitTemplate
  • 监听器容器,用于异步处理入栈消息
  • RabbitAdmin自动声明队列,交换和绑定

简单模式

发送消息

在父pom中引入依赖

<!--AMQP依赖,包含RabbitMQ-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>

更改配置

spring:
  rabbitmq:
    host: 192.168.32.129
    port: 5672
    virtual-host: /
    username: admin
    password: '000'

注入RabbitTemplate并发送消息

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Test
    public void send() {
        String queueName = "hello";
        String message = "hello spring amqp";
        rabbitTemplate.convertAndSend(queueName, message);
    }
接收消息

消息一旦接收,就会从mq删除,RabbitMQ没有回溯功能

同样加配置

/**
 * 接收消息
 */
@Component
public class SpringRabbitListener {

	//指定队列名称直接可以接收
    @RabbitListener(queues = "hello")
    public void listenHello(String message) {
        System.out.println("消费者接收到:\t" + message);
    }
}

工作模式

在这里插入图片描述

发送
@Test
public void send2() throws InterruptedException {
    String queueName = "hello";
    String message = "hello spring amqp _";
    for (int i = 1; i <= 50; i++) {
        rabbitTemplate.convertAndSend(queueName, message + i);
        Thread.sleep(20);
    }
}
接收

默认,消息预取机制,多个消费者会先将队列中的消息分配,然后才消费,这样不能按照消费者的能力分配。所以进行配置,以便消息按能力分配

spring:
  rabbitmq:
    listener:
      simple:
        prefetch: 1 # 每次只能获取一条消息,处理完成才能获取下一个消息

接收消息

@RabbitListener(queues = "hello")
public void listenHello1(String message) throws InterruptedException {
   System.out.println("消费者1 接收到:\t" + message + "\t" + LocalTime.now());
   Thread.sleep(20);
}

@RabbitListener(queues = "hello")
public void listenHello2(String message) throws InterruptedException {
   System.out.println("消费者2 接收到:\t" + message + "\t" + LocalTime.now());
   Thread.sleep(200);
}

广播模式

生产者向交换机发送消息
    @Test
    public void send3() throws InterruptedException {
        String exchangeName = "fanout1";
        String message = "hello spring amqp fanout";
        rabbitTemplate.convertAndSend(exchangeName, "", message);
        Thread.sleep(20);
    }
消费者配置绑定关系

声明交换机队列并进行绑定

/**
 * 广播模式
 */
@Configuration
public class FanoutConfig {

    /**
     * 声明交换机
     */
    @Bean
    public FanoutExchange fanoutExchange() {
        return new FanoutExchange("fanout1");
    }

    /**
     * 声明队列
     */
    @Bean
    public Queue fanoutQueue1() {
        return new Queue("fanout.queue1");
    }

    @Bean
    public Queue fanoutQueue2() {
        return new Queue("fanout.queue2");
    }

    /**
     * 绑定队列和交换机
     */
    @Bean
    public Binding bindingQueue1(FanoutExchange fanoutExchange, Queue fanoutQueue1) {
        return BindingBuilder.bind(fanoutQueue1).to(fanoutExchange);
    }

    @Bean
    public Binding bindingQueue2(FanoutExchange fanoutExchange, Queue fanoutQueue2) {
        return BindingBuilder.bind(fanoutQueue2).to(fanoutExchange);
    }
}
消费者接收消息
//广播模式
    @RabbitListener(queues = "fanout.queue1")
    public void listenHello1(String message) throws InterruptedException {
        System.out.println("消费者1 接收到:\t" + message + "\t" + LocalTime.now());
        Thread.sleep(20);
    }

    @RabbitListener(queues = "fanout.queue2")
    public void listenHello2(String message) throws InterruptedException {
        System.out.println("消费者2 接收到:\t" + message + "\t" + LocalTime.now());
        Thread.sleep(200);
    }

路由模式

在这里插入图片描述

接收消息
//路由模式,直接声明 交换机 队列 bindingkey
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "direct.queue1"),
            exchange = @Exchange(name = "direct1", type = ExchangeTypes.DIRECT),
            key = {"red", "blue"}))
    public void listenDirectQueue1(String message) {
        System.out.println("消费者1接收到:\t" + message + "\t" + LocalTime.now());
    }

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "direct.queue2"),
            exchange = @Exchange(name = "direct1", type = ExchangeTypes.DIRECT),
            key = {"red", "yellow"}))
    public void listenDirectQueue2(String message) {
        System.out.println("消费者2接收到:\t" + message + "\t" + LocalTime.now());
    }
发送消息

写入bindingkey属性

/**
     * 路由模式
     */
    @Test
    public void send4() throws InterruptedException {
        String exchangeName = "direct1";
        String message = "hello spring amqp direct red";
        rabbitTemplate.convertAndSend(exchangeName, "red", message);
    }

主题模式

在这里插入图片描述

接收消息
//主题模式
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "topic.queue1"),
            exchange = @Exchange(name = "topic1", type = ExchangeTypes.TOPIC),
            key = "china.#"
    ))
    public void listenTopicQueue1(String message) {
        System.out.println("消费者1接收到:\t" + message);
    }

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "topic.queue2"),
            exchange = @Exchange(name = "topic1", type = ExchangeTypes.TOPIC),
            key = "#.news"
    ))
    public void listenTopicQueue2(String message) {
        System.out.println("消费者2接收到:\t" + message);
    }
发送消息
/**
     * 主题模式
     */
    @Test
    public void send5() {
        String exchangeName = "topic1";
        String message = "hello spring amqp topic";
        rabbitTemplate.convertAndSend(exchangeName, "china.news", message);
    }

发送Object消息

我们可以直接发送object类型消息,spring默认使用jdk序列化方式,非常复杂而且容易注入,安全性不好。

所有使用Json方式
在这里插入图片描述
使用

  1. 在父工程pom中引入依赖
<!--jackson-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </dependency>
  1. 接 / 发 消息的启动类中加入bean
@Bean
public MessageConverter jsonMessageConverter() {
    return new Jackson2JsonMessageConverter();
}
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

EnndmeRedis

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值