Rabbitmq-1

MQ基本概念

1. MQ概述

MQ全称 Message Queue([kjuː])(消息队列),是在消息的传输过程中保存消息的容器。多用于分布式系统之间进行通信。

(队列是一种容器,用于存放数据的都是容器,存放消息的就是消息队列)

分布式系统的调用:

方式一:直接调用

order

product

account

方式二:间接调用

A将数据存放到中间一个系统,通过中间的系统发送到B

中间系统可以成为中间件MQ

生产者-》中间件《--消费者

MQ是用于存放消息的中间件

被调用者叫生产者 调用者是消费者(微服务中说过)

2. MQ的优势和劣势

2.1 优势

应用解耦:提高系统容错性和可维护性。

异步提速:提升用户体验和系统吞吐量。

削峰填谷:提高系统稳定性。

应用解耦

系统的耦合性越高,容错性就越低,可维护性就越低。

例:订单系统 的时候 依赖于库存系统 支付系统 物流系统 当库存系统发生异常,就有可能导致订单系统发生异常 下单失败

追加系统 x 就只能修改订单系统更改代码 导致维护性比较低

使用 MQ 使得应用间解耦,提升容错性和可维护性

库存系统宕机订单系统影响不大,因为消息已经发送到mq了当库存系统恢复的时候就可以正常使用了。

追加系统的时候跟订单系统无关

已将数据发送到MQ了,直接从MQ中拿就行了,无需更改订单中的代码,可维护性提高

异步提速

一个下单操作耗时:20 + 300 + 300 + 300 = 920ms

用户点击完下单按钮后,需要等待920ms才能得到下单响应,太慢!

用户点击完下单按钮后,只需等待25ms就能得到下单响应 (20 + 5 = 25ms)。

提升用户体验和系统吞吐量(单位时间内处理请求的数目)。

以前920ms处理一个请求,现在25ms处理一个请求,系统的吞吐量(单位时间内访问量)增加

削峰填谷(削峰)

使用了 MQ 之后,限制消费消息的速度为1000,这样一来,高峰期产生的数据势必会被积压在 MQ 中,高峰就被“削”掉了,但是因为消息积压,在高峰期过后的一段时间内,消费消息的速度还是会维持在1000,直到消费完积压的消息,这就叫做“填谷”。

使用MQ后,可以提高系统稳定性。

2.2 劣势

系统可用性降低

系统引入的外部依赖越多,系统稳定性越差。一旦 MQ 宕机,就会对业务造成影响。如何保证MQ的高可用?

系统复杂度提高

MQ 的加入大大增加了系统的复杂度,以前系统间是同步的远程调用,现在是通过 MQ 进行异步调用。如何保证消息没有被重复消费?怎么处理消息丢失情况?那么保证消息传递的顺序性?

一致性问题

A 系统处理完业务,通过 MQ 给B、C、D三个系统发消息数据,如果 B 系统、C 系统处理成功,D 系统处理失败。如何保证消息数据处理的一致性?

2.3 既然 MQ 有优势也有劣势,那么使用 MQ 需要满足什么条件呢?

消费者--》生产者

  1. 生产者不需要从消费者处获得反馈。引入消息队列之前的直接调用,其接口的返回值应该为空,这才让明明下层的动作还没做,上层却当成动作做完了继续往后走,即所谓异步成为了可能。

订单->库存

  1. 容许短暂的不一致性。
  2. 确实是用了有效果。即解耦、提速、削峰这些方面的收益,超过加入MQ,管理MQ这些成本。

3. 常见的MQ产品

目前业界有很多的 MQ 产品,例如 RabbitMQ、RocketMQ、ActiveMQ、Kafka、ZeroMQ、MetaMq等,也有直接使用 Redis 充当消息队列的案例,而这些消息队列产品,各有侧重,在实际选型时,需要结合自身需求及 MQ 产品特征,综合考虑。

RabbitMQ基本介绍

AMQP,即 Advanced Message Queuing Protocol(英[ˈprəʊtəkɒl])(高级消息队列协议),是一个网络协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同的开发语言等条件的限制。2006年,AMQP 规范发布。类比HTTP。

消息队列中间件

exchange 交换机 分发消息 分发到不同的容器 queue 通过路由来处理

queue 容器

routes 路由

生产者 发布消息到exchange exchange 通过不同的路由规则发布/路由 给不同的queue 进行存储 cunsumer通过队列去监听拿到消息进行消费

2007年,Rabbit 技术公司基于 AMQP 标准开发的 RabbitMQ 1.0 发布。RabbitMQ 采用 Erlang 语言开发。Erlang 语言由 Ericson 设计,专门为开发高并发和分布式系统的一种语言,在电信领域使用广泛。

1. RabbitMQ 基础架构

Broker 中间者 服务

procedure 和consumer都是客户端

客户端通过链接和服务端进行通信 所以需要建立起来连接 然后进行通信a

使用channel(管道)节省资源

一个rabbitmq里面有很多的虚拟机 相当于mysql里面有很多数据库,数据库里面有很多表,都是独立的。

每个虚拟机里面有很多的exchange和queue 独立分区的作用

2. RabbitMQ 中的相关概念

Broker:接收和分发消息的应用,RabbitMQ Server就是 Message Broker。

Virtual host:出于多租户和安全因素设计的,把 AMQP 的基本组件划分到一个虚拟的分组中,类似于网络中的 namespace 概念。当多个不同的用户使用同一个 RabbitMQ server 提供的服务时,可以划分出多个vhost,每个用户在自己的 vhost 创建 exchange/queue 等。

Connection:publisher/consumer 和 broker 之间的 TCP 连接。

Channel:如果每一次访问 RabbitMQ 都建立一个 Connection,在消息量大的时候建立 TCP Connection的开销将是巨大的,效率也较低。Channel 是在 connection 内部建立的逻辑连接,如果应用程序支持多线程,通常每个thread创建单独的 channel 进行通讯,AMQP method 包含了channel id 帮助客户端和message broker 识别 channel,所以 channel 之间是完全隔离的。Channel 作为轻量级的 Connection 极大减少了操作系统建立 TCP connection 的开销。

Exchange:message 到达 broker 的第一站,根据分发规则,匹配查询表中的 routing key,分发消息到queue 中去。常用的类型有:

direct (point-to-point)

topic (publish-subscribe)

fanout (multicast)

Queue:消息最终被送到这里等待 consumer 取走

Binding:exchange 和 queue 之间的虚拟连接,binding 中可以包含 routing key。Binding 信息被保存到 exchange 中的查询表中,用于 message 的分发依据

3. RabbitMQ的6 种工作模式

RabbitMQ 提供了 6 种工作模式:

简单模式、work queues、Publish/Subscribe 发布与订阅模式、Routing 路由模式、Topics 主题模式、RPC 远程调用模式(远程调用,不太算 MQ;暂不作介绍)。==

官网对应模式介绍:RabbitMQ Tutorials — RabbitMQ

4. AMQP 和 JMS

MQ是消息通信的模型;实现MQ的大致有两种主流方式:AMQP、JMS。

4.1 AMQP

AMQP是一种协议,更准确的说是一种binary wire-level protocol(链接协议)。这是其和JMS的本质差别,AMQP不从API层进行限定,而是直接定义网络交换的数据格式。

4.2 JMS

JMS即Java消息服务(Java Message Service)应用程序接口,是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。

(规定了消息客户端的一套api的东西,rabbitmq没有遵循规则)

JMS 是 JavaEE 规范中的一种,类比JDBC。

4.3 AMQP与 JMS 区别

JMS是定义了统一的接口,来对消息操作进行统一;AMQP是通过规定协议来统一数据交互的格式。

JMS限定了必须使用Java语言;AMQP只是协议,不规定实现方式,因此是跨语言的。

JMS规定了两种消息模式;而AMQP的消息模式更加丰富

4.4 再谈市场上常见的消息队列

ActiveMQ:基于JMS

ZeroMQ:基于C语言开发

RabbitMQ:基于AMQP协议,erlang语言开发,稳定性好

RocketMQ:基于JMS,阿里巴巴产品

Kafka:类似MQ的产品;分布式消息系统,高吞吐量。

RabbitMQ的安装和配置(1)

RabbitMQ: easy to use, flexible messaging and streaming — RabbitMQ

Linux下载并安装rabbitmq-server-3.6.5-1.noarch.rpm_linux rabbitmq 3.6下载-CSDN博客

1. 安装依赖环境

在线安装依赖环境:

yum -y install build-essential openssl openssl-devel unixODBC unixODBC-devel make gcc gcc-c++ kernel-devel m4 ncurses-devel tk tc xz

2. 安装Erlang

​# 安装

rpm -ivh erlang-22.0.7-1.el7.x86_64.rpm

3. 安装RabbitMQ

#安装依赖的包

rpm -ivh socat-1.7.3.2-2.el7.x86_64.rpm

#安装rabbitmq

rpm -ivh rabbitmq-server-3.7.18-1.el7.noarch.rpm

rpm -ivh erlang-22.0.7-1.el7.x86_64.rpm socat-1.7.3.2-2.el7.x86_64.rpm rabbitmq-server-3.7.18-1.el7.noarch.rpm

启动RabbitMQ

systemctl start rabbitmq-server # 启动服务

systemctl stop rabbitmq-server # 停止服务

systemctl restart rabbitmq-server # 重启服务

systemctl status rabbitmq-server #查看状态

开启管理界面及配置

# 开启管理界面

rabbitmq-plugins enable rabbitmq_management

# 修改默认配置信息

vim /usr/lib/rabbitmq/lib/rabbitmq_server-3.7.18/ebin/rabbit.app

# 比如修改密码、配置等等,例如:loopback_users 中的 <<"guest">>,只保留guest

修改之后重启一下rabbitmq

RabbitMQ的安装和配置(2)

拉取rabbitmq

docker pull rabbitmq:3.7.18

2.启动rabbitmq

docker run -d --name rabbitmq -p15672:15672 -p5672:5672 --privileged=true -v /app/rabbitMQ:/data rabbitmq:3.7.18

3. 查看mq的运行

dcoker ps -a

4. 进入rabbitmq

docker exec -it 009745c3b871 bash

5. 开启web界面管理插件

rabbitmq-plugins enable rabbitmq_management

打开客户端

192.168.221.37:15672

使用guest/guest登录之后出现如下即为安装成功

RabbitMQ入门

1. 搭建工程

1.1 创建工程

生产者

消费者

1.2 添加依赖
<dependency>
    <groupId>com.rabbitmq</groupId>
    <artifactId>amqp-client</artifactId>
    <version>5.6.0</version>
</dependency>

2. 编写生产者

package com.yyl;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;


import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeoutException;


/**
 * @author yuyongli
 */
public class producer {
    public static void main(String[] args)throws IOException, TimeoutException {
        //1.创建工厂
        ConnectionFactory conn=new ConnectionFactory();
        //2.设置参数
        conn.setHost("192.168.159.35");
        conn.setPort(5672);// 5672
        conn.setUsername("yuyongli");//
        conn.setPassword("yuyongli");
        conn.setVirtualHost("/yyl");
        //3.建理连接
        Connection connection = conn.newConnection();
        //4.创建channel
        Channel channel = connection.createChannel();
        //5.创建 队列
        channel.queueDeclare("hello",true,false,false,null);
        //queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
        /**
         * 对应的参数 queue 队列
         * durable 是都为持久化
         * exclusive
         *   是否独占  一个生产者只有一个消费者
         *   管理连接的时候删除队列
         * autodelete 是都自动删除
         * 参数  null
         *
         */
        //6.发送消息


        /*
         *         basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body)
         *         参数:
         *             1. exchange:交换机名称。简单模式下交换机会使用默认的 ""
         *             2. routingKey:路由名称
         *             3. props:配置信息
         *             4. body:发送消息数据
         *
         *
         */
        String body="hello rabbit";
        channel.basicPublish("","hello",null,body.getBytes());


        //关闭资源
        //channel.close();
       // connection.close();
    }
}
查看客户端中的消息队列信息

3. 编写消费者

package com.yyl;


import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;


/**
 * @author yuyongli
 */
public class consumer {
    public static void main(String[] args) throws IOException, TimeoutException {
        //1.创建工厂
        ConnectionFactory conn=new ConnectionFactory();
        //2.设置参数
        conn.setHost("192.168.159.35");
        conn.setPort(5672);
        conn.setUsername("yuyongli");
        conn.setPassword("yuyongli");
        conn.setVirtualHost("/yyl");
        //3.建立连接
        Connection connection = conn.newConnection();
        //4.创建channel
        Channel channel = connection.createChannel();


        //队列
        //自动确认
        //3. consumer对象
       // public String basicConsume(String queue, boolean autoAck, Consumer callback) throws IOException {


        Consumer consumer=new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //打印消息
                System.out.println("consumerTag:"+consumerTag);
                System.out.println("Exchange:"+envelope.getExchange());
                System.out.println("RoutingKey:"+envelope.getRoutingKey());
                System.out.println("properties:"+properties);
                System.out.println("body:"+new String(body));


            }
        };
        channel.basicConsume("hello",true,consumer);
        //消费者主要是为了监听程序  所以无需关闭连接
    }
}

抽取连接的代码

public class ConnectionUtil {


    public static Connection getConnection() throws Exception {
        //创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
           ConnectionFactory conn=new ConnectionFactory();
        //2.设置参数
        conn.setHost("192.168.159.35");
        conn.setPort(5672);
        conn.setUsername("yuyongli");
        conn.setPassword("yuyongli");
        conn.setVirtualHost("/yyl");
        //创建连接
        return connectionFactory.newConnection();
    }
}

RabbitMQ工作模式

官网对应模式介绍:RabbitMQ Tutorials — RabbitMQ

2. Work queues工作队列模式

2.1 模式说明

Work Queues与入门程序的简单模式相比,多了一个或一些消费端,多个消费端共同消费同一个队列中的消息。

应用场景:对于 任务过重或任务较多情况使用工作队列可以提高任务处理的速度。

2.2 代码

Work Queues与入门程序的简单模式的代码是几乎一样的;可以完全复制,并复制多一个消费者进行多个消费者同时消费消息的测试。

生产者
package com.yyl;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
 * @author yuyongli

 */
public class workqueueproceduer {
    public static void main(String[] args)throws IOException, TimeoutException {
        //1.创建工厂
        ConnectionFactory conn=new ConnectionFactory();
        //2.设置参数
        conn.setHost("192.168.159.35");
        conn.setPort(5672);
        conn.setUsername("yuyongli");
        conn.setPassword("yuyongli");
        conn.setVirtualHost("/yyl");
        //3.简历连接
        Connection connection = conn.newConnection();
        //4.创建channel
        Channel channel = connection.createChannel();
        //5.创建 队列
        channel.queueDeclare("workqueue",true,false,false,null);
        //queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
        /**
         * 对应的参数 queue 队列
         * durable 是都为持久化
         * exclusive
         *   是否独占  一个生产者只有一个消费者
         *   管理连接的时候删除队列
         * autodelete 是都自动删除
         * 参数  null
         *
         */
        //6.发送消息
        /*
         *         basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body)
         *         参数:
         *             1. exchange:交换机名称。简单模式下交换机会使用默认的 ""
         *             2. routingKey:路由名称
         *             3. props:配置信息
         *             4. body:发送消息数据
         *
         *
         */
        for(int i=1;i<=10;i++){
            String body=i+"hello rabbit······";
            channel.basicPublish("","workqueue",null,body.getBytes());
        }


        //关闭资源
       // channel.close();
       // connection.close();
    }
}
消费者1:
package com.yyl;


import com.rabbitmq.client.*;


import java.io.IOException;
import java.util.concurrent.TimeoutException;


/**
 * @author yuyongli
 */
public class workqueueconsumer1 {
    public static void main(String[] args) throws IOException, TimeoutException {
        //1.创建工厂
        ConnectionFactory conn=new ConnectionFactory();
        //2.设置参数
        conn.setHost("192.168.159.35");
        conn.setPort(5672);
        conn.setUsername("yuyongli");
        conn.setPassword("yuyongli");
        conn.setVirtualHost("/yyl");
        //3.简历连接
        Connection connection = conn.newConnection();
        //4.创建channel
        Channel channel = connection.createChannel();
        channel.queueDeclare("workqueue",true,false,false,null);
        //队列
        //自动确认
        //3. consumer对象
       // public String basicConsume(String queue, boolean autoAck, Consumer callback) throws IOException {


        Consumer consumer=new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //打印消息
//                System.out.println("consumerTag:"+consumerTag);
//                System.out.println("Exchange:"+envelope.getExchange());
//                System.out.println("RoutingKey:"+envelope.getRoutingKey());
//                System.out.println("properties:"+properties);
                System.out.println("body:"+new String(body));
            }
        };
        channel.basicConsume("workqueue",true,consumer);
        //消费者主要是为了监听程序  所以无需关闭连接
    }
}
 
消费者2:
package com.yyl;


import com.rabbitmq.client.*;


import java.io.IOException;
import java.util.concurrent.TimeoutException;


/**
 * @author yuyongli

 */
public class workqueueconsumer2 {
    public static void main(String[] args) throws IOException, TimeoutException {
        //1.创建工厂
        ConnectionFactory conn=new ConnectionFactory();
        //2.设置参数
        conn.setHost("192.168.159.35");
        conn.setPort(5672);
        conn.setUsername("yuyongli");
        conn.setPassword("yuyongli");
        conn.setVirtualHost("/yyl");
        //3.简历连接
        Connection connection = conn.newConnection();
        //4.创建channel
        Channel channel = connection.createChannel();
        channel.queueDeclare("workqueue",true,false,false,null);
        //队列
        //自动确认
        //3. consumer对象
       // public String basicConsume(String queue, boolean autoAck, Consumer callback) throws IOException {


        Consumer consumer=new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //打印消息
//                System.out.println("consumerTag:"+consumerTag);
//                System.out.println("Exchange:"+envelope.getExchange());
//                System.out.println("RoutingKey:"+envelope.getRoutingKey());
//                System.out.println("properties:"+properties);
                System.out.println("body:"+new String(body));


            }
        };
        channel.basicConsume("workqueue",true,consumer);
        //消费者主要是为了监听程序  所以无需关闭连接
    }
}

在一个队列中如果有多个消费者,那么消费者之间对于同一个消息的关系是竞争的关系。

3. 订阅模式类型☆

订阅模式示例图:

前面2个案例中,只有3个角色:

  • P:生产者,也就是要发送消息的程序
  • C:消费者:消息的接受者,会一直等待消息到来。
  • queue:消息队列,图中红色部分

而在订阅模型中,多了一个exchange角色,而且过程略有变化:

生产者发消息给交换机,交换机将消息路由分发给队列,消费者监听队列接收信息

  • P:生产者,也就是要发送消息的程序,但是不再发送到队列中,而是发给X(交换机)
  • C:消费者,消息的接受者,会一直等待消息到来。
  • Queue:消息队列,接收消息、缓存消息。
  • Exchange:交换机,图中的X。一方面,接收生产者发送的消息。另一方面,知道如何处理消息,例如递交给某个特别队列、递交给所有队列、或是将消息丢弃。到底如何操作,取决于Exchange的类型。Exchange有常见以下3种类型:
    • Fanout:广播,将消息交给所有绑定到交换机的队列
    • Direct:定向,把消息交给符合指定routing key 的队列
    • Topic:通配符,把消息交给符合routing pattern(路由模式) 的队列

Exchange(交换机)只负责转发消息,不具备存储消息的能力,因此如果没有任何队列与Exchange绑定,或者没有符合路由规则的队列,那么消息会丢失!

例子:

消费者1 将日志的消息打印到控制台

消费者2 将日志的信息保存到数据库

代码

生产者:
package com.yyl;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;


import java.io.IOException;
import java.util.concurrent.TimeoutException;


/**
 * @author yuyongli
 */
public class producerPubsub {
    public static void main(String[] args)throws IOException, TimeoutException {
        //1.创建工厂
        ConnectionFactory conn=new ConnectionFactory();
        //2.设置参数
        conn.setHost("192.168.159.35");
        conn.setPort(5672);
        conn.setUsername("yuyongli");
        conn.setPassword("yuyongli");
        conn.setVirtualHost("/yyl");
        //3.建立连接
        Connection connection = conn.newConnection();
        //4.创建channel
        Channel channel = connection.createChannel();
        //5.创建交换机
        /**
         * exchangeDeclare(String exchange, BuiltinExchangeType type, boolean durable, boolean autoDelete, boolean internal, Map<String, Object> arguments)
         * 参数1 交换机
         *    2 类型
         *     DIRECT("direct"), 定向
         *     FANOUT("fanout"), 广播 发送消息到与之绑定的每一个队列
         *     TOPIC("topic"),通配符
         *     HEADERS("headers"); 参数匹配
         *    3 持久化
         *    4 删除
         *    5 内部插件开发
         *    6 参数
         */
        String exchangename="test_fanout";
        channel.exchangeDeclare(exchangename, BuiltinExchangeType.FANOUT,true,false,false,null);
        //6.创建队列
        //
        String queuename="test_fanout_queue1";
        String queuename2="test_fanout_queue2";
        channel.queueDeclare(queuename,true,false,false,null);
        channel.queueDeclare(queuename2,true,false,false,null);


        //7绑定交换机
        //参数
        /**String queue, String exchange, String routingKey
         * 1.队列的名称
         * 2.交换机的名称
         * 3.路由的key  如果类型使用的是fanout 就是""
         */
        channel.queueBind(queuename,exchangename,"");
        channel.queueBind(queuename2,exchangename,"");
        //8 发送消息
        String body="yyl调用了一个方法,这个方法是xxx";
        //String exchange, String routingKey, BasicProperties props, byte[] body
        channel.basicPublish(exchangename,"",null,body.getBytes());


        //关闭资源
        //channel.close();
       // connection.close();
    }
}
 
消费者1:
package com.yyl;


import com.rabbitmq.client.*;


import java.io.IOException;
import java.util.concurrent.TimeoutException;


/**
 * @author yuyongli
 * @Date 2021/4/11
 */
public class pubsubconsumer1 {
    public static void main(String[] args) throws IOException, TimeoutException {
        //1.创建工厂
        ConnectionFactory conn=new ConnectionFactory();
        //2.设置参数
        conn.setHost("192.168.159.35");
        conn.setPort(5672);
        conn.setUsername("yuyongli");
        conn.setPassword("yuyongli");
        conn.setVirtualHost("/yyl");
        //3.简历连接
        Connection connection = conn.newConnection();
        //4.创建channel
        Channel channel = connection.createChannel();
        String queuename="test_fanout_queue1";
        String queuename2="test_fanout_queue2";
        //channel.queueDeclare("workqueue",true,false,false,null);
        //队列
        //自动确认
        //3. consumer对象
       // public String basicConsume(String queue, boolean autoAck, Consumer callback) throws IOException {


        Consumer consumer=new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //打印消息
//                System.out.println("consumerTag:"+consumerTag);
//                System.out.println("Exchange:"+envelope.getExchange());
//                System.out.println("RoutingKey:"+envelope.getRoutingKey());
//                System.out.println("properties:"+properties);
                System.out.println("body:"+new String(body));
                //
                System.out.println("将消息打印到控制台");


            }
        };
        channel.basicConsume(queuename,true,consumer);
        //消费者主要是为了监听程序  所以无需关闭连接
    }
}

消费者2:
package com.yyl;


import com.rabbitmq.client.*;


import java.io.IOException;
import java.util.concurrent.TimeoutException;


/**
 * @author yuyongli
 *
 */
public class pubsubconsumer2 {
    public static void main(String[] args) throws IOException, TimeoutException {
        //1.创建工厂
        ConnectionFactory conn=new ConnectionFactory();
        //2.设置参数
        conn.setHost("192.168.159.35");
        conn.setPort(5672);
        conn.setUsername("yuyongli");
        conn.setPassword("yuyongli");
        conn.setVirtualHost("/yyl");
        //3.简历连接
        Connection connection = conn.newConnection();
        //4.创建channel
        Channel channel = connection.createChannel();
        String queuename="test_fanout_queue1";
        String queuename2="test_fanout_queue2";
        //channel.queueDeclare("workqueue",true,false,false,null);
        //队列
        //自动确认
        //3. consumer对象
       // public String basicConsume(String queue, boolean autoAck, Consumer callback) throws IOException {


        Consumer consumer=new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //打印消息
//                System.out.println("consumerTag:"+consumerTag);
//                System.out.println("Exchange:"+envelope.getExchange());
//                System.out.println("RoutingKey:"+envelope.getRoutingKey());
//                System.out.println("properties:"+properties);
                System.out.println("body:"+new String(body));
                //
                System.out.println("将消息保存到数据库");


            }
        };
        channel.basicConsume(queuename2,true,consumer);
        //消费者主要是为了监听程序  所以无需关闭连接
    }
}
 
总结:

发送消息的时候声明交换机,并且设置交换机的类型为fanout,当指定这个类型之后对应的routingkey的值为"" 最后交换机将消息发送给它绑定的每一个队列,每个消费者只监听自己的队列

交换机需要与队列进行绑定,绑定之后;一个消息可以被多个消费者都收到。

发布订阅模式与工作队列模式的区别

1、工作队列模式不用定义交换机,而发布/订阅模式需要定义交换机。

2、发布/订阅模式的生产方是面向交换机发送消息,工作队列模式的生产方是面向队列发送消息(底层使用默认交换机)。

3、发布/订阅模式需要设置队列和交换机的绑定,工作队列模式不需要设置,实际上工作队列模式会将队列绑 定到默认的交换机 。

4. Routing路由模式

4.1 模式说明

路由模式特点:

  • 队列与交换机的绑定,不能是任意绑定了,而是要指定一个RoutingKey(路由key)
  • 消息的发送方在 向Exchange发送消息时,也必须指定消息的 RoutingKey
  • Exchange不再把消息交给每一个绑定的队列,而是根据消息的Routing Key进行判断,只有队列的Routingkey与消息的 Routing key完全一致,才会接收到消息

图解:

  • P:生产者,向Exchange发送消息,发送消息时,会指定一个routing key。
  • X:Exchange(交换机),接收生产者的消息,然后把消息递交给 与routing key完全匹配的队列
  • C1:消费者,其所在队列指定了需要routing key 为 error 的消息
  • C2:消费者,其所在队列指定了需要routing key 为 info、error、warning 的消息

4.2 代码

在编码上与 Publish/Subscribe发布与订阅模式 的区别是交换机的类型为:Direct,还有队列绑定交换机的时候需要指定routing key。

生产者
package com.yyl;


import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;


import java.io.IOException;
import java.util.concurrent.TimeoutException;


/**
 * @author yuyongli
 * @Date 2021/4/11
 */
public class producerroting {
    public static void main(String[] args)throws IOException, TimeoutException {
        //1.创建工厂
        ConnectionFactory conn=new ConnectionFactory();
        //2.设置参数
        conn.setHost("192.168.159.35");
        conn.setPort(5672);
        conn.setUsername("yuyongli");
        conn.setPassword("yuyongli");
        conn.setVirtualHost("/yyl");
        //3.简历连接
        Connection connection = conn.newConnection();
        //4.创建channel
        Channel channel = connection.createChannel();
        //5.创建交换机
        /**
         * exchangeDeclare(String exchange, BuiltinExchangeType type, boolean durable, boolean autoDelete, boolean internal, Map<String, Object> arguments)
         * 参数1 交换机
         *    2 类型
         *     DIRECT("direct"), 定向
         *     FANOUT("fanout"), 广播 发送消息到与之绑定的每一个队列
         *     TOPIC("topic"),通配符
         *     HEADERS("headers"); 参数匹配
         *    3 持久化
         *    4 删除
         *    5 内部插件开发
         *    6 参数
         */
        String exchangename="test_direct";
        channel.exchangeDeclare(exchangename, BuiltinExchangeType.DIRECT,true,false,false,null);
        //6.创建队列
        //
        String queuename="test_direct_queue1";
        String queuename2="test_direct_queue2";
        channel.queueDeclare(queuename,true,false,false,null);
        channel.queueDeclare(queuename2,true,false,false,null);


        //7绑定交换机
        //参数
        /**String queue, String exchange, String routingKey
         * 1.队列的名称
         * 2.交换机的名称
         * 3.路由的key  如果类型使用的是direct 就需要对应上
         */
        channel.queueBind(queuename,exchangename,"error"); // error
        channel.queueBind(queuename2,exchangename,"error");// error info 
        channel.queueBind(queuename2,exchangename,"info");
        channel.queueBind(queuename2,exchangename,"warning");
        channel.queueBind(queuename2,exchangename,"debug");
        //8 发送消息
        String body="yyl调用了一个方法,这个方法是delete,日志的级别是info";
        //String exchange, String routingKey, BasicProperties props, byte[] body
        channel.basicPublish(exchangename,"info",null,body.getBytes());


        //关闭资源
        //channel.close();
       // connection.close();
    }
}
 

消费者1
package com.yyl;


import com.rabbitmq.client.*;


import java.io.IOException;
import java.util.concurrent.TimeoutException;


/**
 * @author yuyongli
 * @Date 2021/4/11
 */
public class routingconsumer1 {
    public static void main(String[] args) throws IOException, TimeoutException {
        //1.创建工厂
        ConnectionFactory conn=new ConnectionFactory();
        //2.设置参数
        conn.setHost("192.168.159.35");
        conn.setPort(5672);
        conn.setUsername("yuyongli");
        conn.setPassword("yuyongli");
        conn.setVirtualHost("/yyl");
        //3.简历连接
        Connection connection = conn.newConnection();
        //4.创建channel
        Channel channel = connection.createChannel();
        String queuename="test_direct_queue1";
        String queuename2="test_direct_queue2";
        //channel.queueDeclare("workqueue",true,false,false,null);
        //队列
        //自动确认
        //3. consumer对象
       // public String basicConsume(String queue, boolean autoAck, Consumer callback) throws IOException {


        Consumer consumer=new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //打印消息
//                System.out.println("consumerTag:"+consumerTag);
//                System.out.println("Exchange:"+envelope.getExchange());
//                System.out.println("RoutingKey:"+envelope.getRoutingKey());
//                System.out.println("properties:"+properties);
                System.out.println("body:"+new String(body));
                //
                System.out.println("将消息打印到控制台");


            }
        };
        channel.basicConsume(queuename,true,consumer);
        //消费者主要是为了监听程序  所以无需关闭连接
    }
}
 
 
消费者2
package com.yyl;


import com.rabbitmq.client.*;


import java.io.IOException;
import java.util.concurrent.TimeoutException;


/**
 * @author yuyongli
 * @Date 2021/4/11
 */
public class routingconsumer2 {
    public static void main(String[] args) throws IOException, TimeoutException {
        //1.创建工厂
        ConnectionFactory conn=new ConnectionFactory();
        //2.设置参数
        conn.setHost("192.168.159.35");
        conn.setPort(5672);
        conn.setUsername("yuyongli");
        conn.setPassword("yuyongli");
        conn.setVirtualHost("/yyl");
        //3.简历连接
        Connection connection = conn.newConnection();
        //4.创建channel
        Channel channel = connection.createChannel();
        String queuename="test_direct_queue1";
        String queuename2="test_direct_queue2";
        //channel.queueDeclare("workqueue",true,false,false,null);
        //队列
        //自动确认
        //3. consumer对象
       // public String basicConsume(String queue, boolean autoAck, Consumer callback) throws IOException {


        Consumer consumer=new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //打印消息
//                System.out.println("consumerTag:"+consumerTag);
//                System.out.println("Exchange:"+envelope.getExchange());
//                System.out.println("RoutingKey:"+envelope.getRoutingKey());
//                System.out.println("properties:"+properties);
                System.out.println("body:"+new String(body));
                //
                System.out.println("将消息打印到控制台");


            }
        };
        channel.basicConsume(queuename2,true,consumer);
        //消费者主要是为了监听程序  所以无需关闭连接
    }
}
 
测试:

总结:

Routing模式要求队列在绑定交换机时要指定routing key,消息会转发到符合routing key的队列。

5. Topics通配符模式

5.1. 模式说明

Topic类型与Direct相比,都是可以根据RoutingKey把消息路由到不同的队列。只不过Topic类型Exchange可以让队列在绑定Routing key 的时候使用通配符

Routingkey 一般都是有一个或多个单词组成,多个单词之间以”.”分割,例如: item.insert

通配符规则:

#:匹配一个或多个词   

*:匹配不多不少恰好1个词   test.* test.insert

举例:

item.#:能够匹配item.insert.abc 或者 item.insert

item.*:只能匹配item.insert 

图解:

  • 红色Queue:绑定的是usa.# ,因此凡是以 usa.开头的routing key 都会被匹配到
  • 黄色Queue:绑定的是#.news ,因此凡是以 .news结尾的 routing key 都会被匹配

5.2. 代码

所有的error级别的日志以及订单系统产生的日志全部让队列1接收

error

order.#

生产者
package com.yyl;


import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;


import java.io.IOException;
import java.util.concurrent.TimeoutException;


/**
 * @author yuyongli
 * @Date 2021/4/11
 */
public class producertopics {
    public static void main(String[] args)throws IOException, TimeoutException {
        //1.创建工厂
        ConnectionFactory conn=new ConnectionFactory();
        //2.设置参数
        conn.setHost("192.168.159.35");
        conn.setPort(5672);
        conn.setUsername("yuyongli");
        conn.setPassword("yuyongli");
        conn.setVirtualHost("/yyl");
        //3.简历连接
        Connection connection = conn.newConnection();
        //4.创建channel
        Channel channel = connection.createChannel();
        //5.创建交换机
        /**
         * exchangeDeclare(String exchange, BuiltinExchangeType type, boolean durable, boolean autoDelete, boolean internal, Map<String, Object> arguments)
         * 参数1 交换机
         *    2 类型
         *     DIRECT("direct"), 定向
         *     FANOUT("fanout"), 广播 发送消息到与之绑定的每一个队列
         *     TOPIC("topic"),通配符
         *     HEADERS("headers"); 参数匹配
         *    3 持久化
         *    4 删除
         *    5 内部插件开发
         *    6 参数
         */
        String exchangename="test_topic";
        channel.exchangeDeclare(exchangename, BuiltinExchangeType.TOPIC,true,false,false,null);
        //6.创建队列
        //
        String queuename="test_topic_queue1";
        String queuename2="test_topic_queue2";
        channel.queueDeclare(queuename,true,false,false,null);
        channel.queueDeclare(queuename2,true,false,false,null);


        //7绑定交换机
        //参数
        /**String queue, String exchange, String routingKey
         * 1.队列的名称
         * 2.交换机的名称
         * 3.路由的key  如果类型使用的是direct 就需要对应上
         */
        //order的所有的日志 以及error级别的 信息全部都保存到队列1中
        //任意的消息全部都需要保存到 队列2中
        channel.queueBind(queuename,exchangename,"order.*");
        channel.queueBind(queuename,exchangename,"*.error");
        channel.queueBind(queuename2,exchangename,"*.*");
        //8 发送消息
        String body="yyl调用了一个方法,这个方法是delete,日志的级别是info";
        //String exchange, String routingKey, BasicProperties props, byte[] body
        channel.basicPublish(exchangename,"goods.info",null,body.getBytes());


        //关闭资源
        //channel.close();
       // connection.close();
    }
}
 

消费者1
package com.yyl;


import com.rabbitmq.client.*;


import java.io.IOException;
import java.util.concurrent.TimeoutException;


/**
 * @author yuyongli
 * @Date 2021/4/11
 */
public class topicconsumer1 {
    public static void main(String[] args) throws IOException, TimeoutException {
        //1.创建工厂
        ConnectionFactory conn=new ConnectionFactory();
        //2.设置参数
        conn.setHost("192.168.159.35");
        conn.setPort(5672);
        conn.setUsername("yuyongli");
        conn.setPassword("yuyongli");
        conn.setVirtualHost("/yyl");
        //3.简历连接
        Connection connection = conn.newConnection();
        //4.创建channel
        Channel channel = connection.createChannel();
        String queuename="test_topic_queue1";
        String queuename2="test_topic_queue2";
        //channel.queueDeclare("workqueue",true,false,false,null);
        //队列
        //自动确认
        //3. consumer对象
       // public String basicConsume(String queue, boolean autoAck, Consumer callback) throws IOException {


        Consumer consumer=new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //打印消息
//                System.out.println("consumerTag:"+consumerTag);
//                System.out.println("Exchange:"+envelope.getExchange());
//                System.out.println("RoutingKey:"+envelope.getRoutingKey());
//                System.out.println("properties:"+properties);
                System.out.println("body:"+new String(body));
                //
                System.out.println("将消息保存到数据库中");


            }
        };
        channel.basicConsume(queuename,true,consumer);
        //消费者主要是为了监听程序  所以无需关闭连接
    }
}
 
消费者2
package com.yyl;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class topicconsumer2 {
    public static void main(String[] args) throws IOException, TimeoutException {
        //1.创建工厂
        ConnectionFactory conn=new ConnectionFactory();
        //2.设置参数
        conn.setHost("192.168.159.35");
        conn.setPort(5672);
        conn.setUsername("yuyongli");
        conn.setPassword("yuyongli");
        conn.setVirtualHost("/yyl");
        //3.简历连接
        Connection connection = conn.newConnection();
        //4.创建channel
        Channel channel = connection.createChannel();
        String queuename="test_topic_queue1";
        String queuename2="test_topic_queue2";
        //channel.queueDeclare("workqueue",true,false,false,null);
        //队列
        //自动确认
        //3. consumer对象
       // public String basicConsume(String queue, boolean autoAck, Consumer callback) throws IOException {
        Consumer consumer=new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //打印消息
//                System.out.println("consumerTag:"+consumerTag);
//                System.out.println("Exchange:"+envelope.getExchange());
//                System.out.println("RoutingKey:"+envelope.getRoutingKey());
//                System.out.println("properties:"+properties);
                System.out.println("body:"+new String(body));
                //
                System.out.println("将消息打印到控制台");
            }
        };
        channel.basicConsume(queuename2,true,consumer);
        //消费者主要是为了监听程序  所以无需关闭连接
    }
}

测试

总结

Topic主题模式可以实现 Publish/Subscribe发布与订阅模式Routing路由模式 的功能;只是Topic在配置routing key 的时候可以使用通配符,显得更加灵活。

6. 模式总结

RabbitMQ工作模式:

1、简单模式 HelloWorld

一个生产者、一个消费者,不需要设置交换机(使用默认的交换机)

`2、工作队列模式 Work Queue

一个生产者、多个消费者(竞争关系),不需要设置交换机(使用默认的交换机)

3、发布订阅模式 Publish/subscribe

需要设置类型为fanout的交换机,并且交换机和队列进行绑定,当发送消息到交换机后,交换机会将消息发送到绑定的队列

4、路由模式 Routing

需要设置类型为direct的交换机,交换机和队列进行绑定,并且指定routing key,当发送消息到交换机后,交换机会根据routing key将消息发送到对应的队列

5、通配符模式 Topic

需要设置类型为topic的交换机,交换机和队列进行绑定,并且指定通配符方式的routing key,当发送消息到交换机后,交换机会根据routing key将消息发送到对应的队列

Spring Boot整合RabbitMQ

1. 简介

在Spring项目中,可以使用Spring-Rabbit去操作RabbitMQ

尤其是在spring boot项目中只需要引入对应的amqp启动器依赖即可,方便的使用RabbitTemplate发送消息,使用注解接收消息。

一般在开发过程中:

生产者工程:

  1. application.yml文件配置相关信息;
  2. 在生产者工程中编写配置类,用于创建交换机和队列,并进行绑定
  3. 注入RabbitTemplate对象,通过RabbitTemplate对象发送消息到交换机

消费者工程:

  1. application.yml文件配置相关信息
  2. 创建消息处理类,用于接收队列中的消息并进行处理

2.搭建生产者工程(适配类型)

2.1 创建工程

创建生产者工程 springboot-rabbitmq-producer

2.2 添加依赖

修改pom.xml文件内容为如下:

<?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>com.yyl</groupId>
    <artifactId>producer-springboot</artifactId>
    <version>1.0-SNAPSHOT</version>
​
    <!--1. 父工程依赖 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.4.RELEASE</version>
    </parent>
​
    <dependencies>
        <!--2. rabbitmq-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
    </dependencies>

</project>

2.3 启动类

package com.yyl;


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;


@SpringBootApplication
public class ProducerApplication {


    public static void main(String[] args) {
        SpringApplication.run(ProducerApplication.class);
    }
}

2.4 配置RabbitMQ

配置文件

创建application.yml,内容如下:

# 配置RabbitMQ的基本信息  ip 端口 username  password..
spring:
  rabbitmq:
    host: 192.168.159.35 # ip
    port: 5672
    username: yuyongli
    password: yuyongli
    virtual-host: /yyl
绑定交换机和队列

创建RabbitMQ队列与交换机绑定的配置类com.yyl.rabbitmq.config.RabbitMQConfig

package yyl.config;
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RabbitMqConfig {
    //设置交换机的名字  和 队列的名字
    public static  final String EXCHANGE_NAME="exchange_topic_name";
    public static  final String QUEUE_NAME="queue_topic_name1";
    public static  final String QUEUE_NAME_2="queue_topic_name2";
    //将交换机这只成为一个bean注入到Springboot里面
    @Bean("topicExchange")
    public Exchange topicExchange(){
        //可以持久化的一个交换机
        return ExchangeBuilder.topicExchange(EXCHANGE_NAME).durable(true).build();
    }
    /**
     * 队列
     */
    @Bean("topicQueue")
    public Queue topicQueue(){
        return QueueBuilder.durable(QUEUE_NAME).build();
    }
    @Bean("topicQueue2")
    public Queue topicQueue2(){
        return QueueBuilder.durable(QUEUE_NAME_2).build();
    }
    /**
     * 将队列和交换机绑定到一起
     */
    @Bean
    public Binding exchangeWithQueue(@Qualifier("topicExchange") Exchange exchage, @Qualifier("topicQueue") Queue queue){
        //保存到数据库中
        //将交换机和队列绑定在一起
        return BindingBuilder.bind(queue).to(exchage).with("*.error").noargs();
    }
    @Bean
    public Binding exchangeWithQueue2(@Qualifier("topicExchange") Exchange exchage, @Qualifier("topicQueue2") Queue queue){
        //输出到控制台中
        //将交换机和队列绑定在一起
        return BindingBuilder.bind(queue).to(exchage).with("*.*").noargs();
    }
}

3. 搭建消费者工程1

3.1 创建工程

创建消费者工程springboot-rabbitmq-consumer

修改pom.xml文件内容为如下:

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

3.3 启动类

package com.yyl.rabbitmq;
​
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
​
@SpringBootApplication
public class ConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class);
    }
}

3.4 配置RabbitMQ

创建application.yml,内容如下:

# 配置RabbitMQ的基本信息  ip 端口 username  password..
spring:
  rabbitmq:
    host 192.168.159.35# ip
    port: 5672
    username: yuyongli
    password: yuyongli
    virtual-host: /yyl

3.5 消息监听处理类

编写消息监听器com.yyl.rabbitmq.listener.RabbimtMQListener

package com.yyl.listen;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class RabbitMqListener {
    //监听队列的消息
    @RabbitListener(queues = "queue_topic_name1")
    public void listenQueue(Message message){
        System.out.println("监听的error级别的日志,需要保存到数据库中======="+new String(message.getBody()));//message.getBody()
    }
}

4.搭建消费者工程2

启动类,配置文件跟消费者1是一样的,仅需要配置监听类即可

4.1 消息监听处理类

package yyl.listen;


import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;


/**
 * @author yuyongli
 */
@Component
public class RabbitMqListener {


    //监听队列的xiaoxi
    @RabbitListener(queues = "queue_topic_name2")
    public void listenQueue(Message message){
        System.out.println("监听的是所有模块的日志,需要输出到控制台上面,日子的信息是======="+new String(message.getBody()));//message.getBody()
    }
}

5. 测试

在生产者工程springboot-rabbitmq-producer中创建测试类,发送消息:

package test;


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.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import yyl.ProducerApplication;
import yyl.config.RabbitMqConfig;


import javax.annotation.Resource;


@SpringBootTest
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = ProducerApplication.class)
public class ProcedureTest {


    @Resource
    private RabbitTemplate rabbitTemplate;


    @Test
    public void  testSendMesssage(){
        //将消息发送到队列里面
        rabbitTemplate.convertAndSend(RabbitMqConfig.EXCHANGE_NAME,"test.error","我是test模块中,日志级别为error的信息");
    }


}
 

测试:

先运行上述测试程序(交换机和队列才能先被声明和绑定),然后启动消费者;在消费者工程springboot-rabbitmq-consumer中控制台查看是否接收到对应消息。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值