RabbitMQ
第一章 消息中间件概述-了解
#1、 什么是消息中间件
#(1)MQ是什么
MQ全称为Message Queue,消息队列是应用程序和应用程序之间的通信方法。
先进先出
用户订购了商品id为1的good,系统订单表会将id=1的商品信息写入订单数据库中,但是订单表内没有id=1的信息无法找到,于是发送请求给goods表,goods表去数据库内查询到id=1的商品信息后返回给order表,最后order将该信息存入数据库订单表中。
原先:ip:port/order/add?goodsId=1
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XNnPrE5d-1682215219529)(null)]
现在:异步调用
当有几十万用户同时进行商品的订购时,只要order将订单号或者上坪id写入了数据库订单表,就会告诉用户订购成功。然后会将这些订购数据信息存入MQ,且goods表会监听MQ,按照1w/s的速度去按照订购的信息进行修改数据库存的数据。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jBHqdkJD-1682215220041)(null)]
#(2)为什么使用MQ–面试
在项目中,可将一些无需即时返回且耗时的操作提取出来,进行异步处理,而这种异步处理的方式大大的节省了服务器的请求响应时间,从而提高了系统的吞吐量。
现在,应用开发和部署—微服务
3)MQ优势
#a、应用解耦
MQ相当于一个中介,生产方通过MQ与消费方交互,它将应用程序进行解耦合。我们将订单模块和支付,物流,商品库存等模块分开操作,降低耦合性。让订单只做对数据库订单表信息操作和对mq发送数据。其他的也各司其职,互不干涉。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OpZNaIvg-1682215220097)(null)]
#b、异步提速
将不需要同步处理的并且耗时长的操作由消息队列通知消息接收方进行异步处理。提高了应用程序的响应时间。
如果不用mq,用户请求发送过来后,对数据库订单进行修改100ms,对商品库存修改100ms,支付功能200ms,物流系统100ms,总共要花费500ms。
如果使用mq,花费100ms对数据库订单修改,10ms发送数据给mq。mq会同时发送三个请求给后三个模块让他们完成功能(只用10ms),那么最多只是用200ms。这样只用320ms。速度更快。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oIS3FYUL-1682215220204)(null)]
order_table status 0 已下单 status 1 支付成功 status 2 已通知商家发货 status 3 商家发货 status 4 已经收货
#c、削峰填谷
如订单系统,在下单的时候就会往数据库写数据。但是数据库只能支撑每秒1000左右的并发写入,并发量再高就容易宕机。低峰期的时候并发也就100多个,但是在高峰期时候,并发量会突然激增到5000以上,这个时候数据库肯定卡死了。
消息被MQ保存起来了,然后系统就可以按照自己的消费能力来消费,比如每秒1000个数据,这样慢慢写入数据库,这样就不会卡死数据库了。比如早上突然五千用户订购了,不是天天都那么多用户,此时mq将消息保存,系统按照自己的速度一步步慢慢写入,再平时不忙的时候就写入完成了。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DRqmVbWM-1682215221907)(null)]
但是使用了MQ之后,限制消费消息的速度为1000,但是这样一来,高峰期产生的数据势必会被积压在MQ中,高峰就被“削”掉了。但是因为消息积压,在高峰期过后的一段时间内,消费消息的速度还是会维持在1000QPS,直到消费完积压的消息,这就叫做“填谷”
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l80iVJfG-1682215220259)(null)]
#(4)MQ劣势
- 系统可用性降低
系统引入的外部依赖越多,系统稳定性越差。一旦 MQ 宕机,就会对业务造成影响。如何保证MQ的高可用?
- 系统复杂度提高
MQ 的加入大大增加了系统的复杂度,以前系统间是同步的远程调用,现在是通过 MQ 进行异步调用。如何
保证消息没有被重复消费?怎么处理消息丢失情况?那么保证消息传递的顺序性?
- 一致性问题
A 系统处理完业务,通过 MQ 给B、C、D三个系统发消息数据,如果 B 系统、C 系统处理成功,D 系统处理
失败。如何保证消息数据处理的一致性。
最终一致性。
#(5)什么时候用MQ
① 生产者不需要从消费者处获得反馈。引入消息队列之前的直接调用,其接口的返回值应该为空,这才让明 明下层的动作还没做,上层却当成动作做完了继续往后走,即所谓异步成为了可能。
② 容许短暂的不一致性。
③ 确实是用了有效果。即解耦、提速、削峰这些方面的收益,超过加入MQ,管理MQ这些成本、
#2、AMQP 和 JMS
MQ是消息通信的模型;实现MQ的大致有两种主流方式:AMQP(协议)、JMS(java的api)。
#(1)AMQP
AMQP是一种协议,更准确的说是一种binary wire-level protocol(链接协议)。这是其和JMS的本质差别,AMQP不从API层进行限定,而是直接定义网络交换的数据格式。
#(2)JMS
JMS即Java消息服务(JavaMessage Service)应用程序接口,是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。
#(3)AMQP 与 JMS 区别
- JMS是定义了统一的接口,来对消息操作进行统一;AMQP是通过规定协议来统一数据交互的格式
- JMS限定了必须使用Java语言;AMQP只是协议,不规定实现方式,因此是跨语言的。
- JMS规定了两种消息模式;而AMQP的消息模式更加丰富。
#3、消息队列产品
市场上常见的消息队列有如下:
- ActiveMQ:基于JMS
- ZeroMQ:基于C语言开发
- RabbitMQ:基于AMQP协议,erlang语言开发,稳定性好
- RocketMQ:基于JMS,阿里巴巴产品
- Kafka:类似MQ的产品;分布式消息系统,高吞吐量
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yzZ9Z9En-1682215220356)(null)]
4、 RabbitMQ
#(1)RabbitMQ特点:
- 1、使用简单,功能强大。
- 2、基于AMQP协议。 跨语言 c node.js->mq->java python
- 3、社区活跃,文档完善。
- 4、高并发性能好,这主要得益于Erlang语言。 c 底层语言,性能强。java 好开发。构建一个web。
- 5、Spring Boot默认已集成RabbitMQ
#(2)其它相关术语
AMQP:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NrXsucQR-1682215220409)(null)]
JMS :
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TeBU02aI-1682215220504)(null)]
总结:
JMS是java提供的一套消息服务API标准,其目的是为所有的java应用程序提供统一的消息通信的标准,类似java的jdbc,只要遵循jms标准的应用程序之间都可以进行消息通信。
它和AMQP有什么 不同,jms是java语言专属的消息服务标准,它是在api层定义标准,并且只能用于java应用;而AMQP是在协议层定义的标准,是跨语言的 。
(3)工作模式
RabbitMQ提供了6种模式:简单模式,work模式,Publish/Subscribe发布与订阅模式,Routing路由模式,Topics主题模式,RPC远程调用模式(远程调用,不太算MQ;暂不作介绍);
官网对应模式介绍:https://www.rabbitmq.com/getstarted.html
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JL9aking-1682215220557)(null)]
#第二章 RabbitMQ工作原理–重点!
重点看这张图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9MKCGlkI-1682215220653)(null)]
http 三次握手 四次挥手(短链接 ,太耗性能) 所以用长连接
一个消费者监听“一个”“队列”
- Broker:消息队列服务进程,此进程包括两个部分:Exchange和Queue。部署了rabbitmq的机器就是一个broker
- Exchange:消息队列交换机,按一定的规则将消息路由转发到某个队列,对消息进行过虑。
- Queue:消息队列,存储消息的队列,消息到达队列并转发给指定的消费方。
- Producer:消息生产者,即生产方客户端,生产方客户端将消息发送到MQ。
- Consumer:消息消费者,即消费方客户端,接收MQ转发的消息。
消息发布接收流程:
-----发送消息-----
1、生产者和Broker建立TCP连接。
2、生产者和Broker建立通道。
3、生产者通过通道消息发送给Broker,由Exchange将消息进行转发。
4、Exchange将消息转发到指定的Queue(队列)
----接收消息-----
1、消费者和Broker建立TCP连接
2、消费者和Broker建立通道
3、消费者监听指定的Queue(队列)
4、当有消息到达Queue时Broker默认将消息推送给消费者。
5、消费者接收到消息
#第四章 RabbitMQ入门
1、搭建示例工程
#(1)创建工程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aKQdJPhY-1682215218577)(C:\Users\陈刚\AppData\Roaming\Typora\typora-user-images\image-20230102113351726.png)]
(2)添加依赖
往rabbitMQ的pom.xml文件中添加如下依赖:
<dependencies>
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.6.0</version>
</dependency>
</dependencies>
2、 编写生产者
producer工程中,编写消息生产者com.ydlclass.rabbitmq.simple.Producer
public class Producer {
public static void main(String[] args) throws IOException, TimeoutException {
//1.创建连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
//连接的ip
connectionFactory.setHost("localhost");
//连接的端口:
connectionFactory.setPort(5672);
//设置虚拟主机
connectionFactory.setVirtualHost("/");
//设置用户名:
connectionFactory.setUsername("cgboy");
connectionFactory.setPassword("cgboy");
//2.创建长连接
Connection connection = connectionFactory.newConnection();
//3.创建channel
Channel channel = connection.createChannel();
//声明队列
//String queue,队列名
//boolean durable,持久化
//boolean exclusive 排他的(当前队列使用,其他的队列就不能使用)
//boolean autoDelete 自动删除
//Map<String,Object> arguments 属性
channel.queueDeclare("ydlqueue",true,false,false,null);
//4.发消息
//String exchange 交换机
//String routingKey 路由键
//AMQP.BasicProperties props 属性
//byte[] body 消息
String msg = "hello rabbitMQ";
channel.basicPublish("","ydlqueue",null,msg.getBytes());
//5.关闭连接
channel.close();
connection.close();
}
在执行上述的消息发送之后;可以登录rabbitMQ的管理控制台,可以发现队列和其消息:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kzgAhftL-1682215218577)(C:\Users\陈刚\AppData\Roaming\Typora\typora-user-images\image-20230102113536273.png)]
查看消息:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WPaiSw5a-1682215218577)(C:\Users\陈刚\AppData\Roaming\Typora\typora-user-images\image-20230102113608569.png)]
3、 编写消费者
public class Consumer {
private static final String QUEUE = "ydlqueue";
public static void main(String[] args) throws Exception {
//1创建连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
//主机地址;默认为 localhost
connectionFactory.setHost("localhost");
//连接端口;默认为 5672
connectionFactory.setPort(5672);
//虚拟主机名称;默认为 /
connectionFactory.setVirtualHost("/");
//连接用户名;默认为guest
connectionFactory.setUsername("cgboy");
//连接密码;默认为guest
connectionFactory.setPassword("cgboy");
//2创建连接
Connection connection = connectionFactory.newConnection();
//3创建频道
Channel channel = connection.createChannel();
//6声明(创建)队列
/**
* 参数1:队列名称
* 参数2:是否定义持久化队列
* 参数3:是否独占本次连接
* 参数4:是否在不使用的时候自动删除队列
* 参数5:队列其它参数
*/
channel.queueDeclare(QUEUE, true, false, false, null);
//5创建消费者;并设置消息处理
DefaultConsumer consumer = new DefaultConsumer(channel) {
@Override
/**
* consumerTag 消息者标签,在channel.basicConsume时候可以指定
* envelope 消息包的内容,可从中获取消息id,消息routingkey,交换机,消息和重传标志(收到消息失败后是否需要重新发送)
* properties 属性信息
* body 消息
*/
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
//消费者标签
System.out.println("消费者标签为:" + consumerTag);
//路由key
System.out.println("路由key为:" + envelope.getRoutingKey());
//交换机
System.out.println("交换机为:" + envelope.getExchange());
//消息id
System.out.println("消息id为:" + envelope.getDeliveryTag());
//收到的消息
System.out.println("接收到的消息为:" + new String(body, "utf-8"));
}
};
//4监听消息
/**
* 参数1:队列名称
* 参数2:是否自动确认,设置为true为表示消息接收到自动向mq回复接收到了,mq接收到回复会删除消息,设置为false则需要手动确认
* 参数3:消息接收到后回调
*/
channel.basicConsume(QUEUE, true, consumer);
//不关闭资源,应该一直监听消息
//channel.close();
//connection.close();
}
}
4、小结
上述的入门案例中中其实使用的是如下的简单模式:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NsW7baAi-1682215220730)(null)]
在上图的模型中,有以下概念:
- P:生产者,也就是要发送消息的程序
- C:消费者:消息的接受者,会一直等待消息到来。
- queue:消息队列,图中红色部分。类似一个邮箱,可以缓存消息;生产者向其中投递消息,消费者从其中取出消息。
#第五章 RabbitMQ工作模式–重点
#1、Work queues工作队列模式(包工头)
在生产者设置发10次消息:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8qTrUA3L-1682215218578)(C:\Users\陈刚\AppData\Roaming\Typora\typora-user-images\image-20230102160403408.png)]
随后设置能多开多个消费者:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MpToxy3a-1682215218578)(C:\Users\陈刚\AppData\Roaming\Typora\typora-user-images\image-20230102160439145.png)]
启动生产者,随后开启两个消费者后显示:
生产者发送的10次消息,被两个消费者争抢着拿到输出到各自的控制台
2、publish/subscribe 订阅发布模式类型(微博)
atm取100元,短信,邮件。
订阅模式示例图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aMUA7aZ3-1682215220854)(null)]
前面2个案例中,只有3个角色:
- P:生产者,也就是要发送消息的程序
- C:消费者:消息的接受者,会一直等待消息到来。
- queue:消息队列,图中红色部分
而在订阅模型中,多了一个exchange角色,而且过程略有变化:
- P:生产者,也就是要发送消息的程序,但是不再发送到队列中,而是发给X(交换机)
- C:消费者,消息的接受者,会一直等待消息到来。
- Queue:消息队列,接收消息、缓存消息。
- Exchange:交换机,图中的X。一方面,接收生产者发送的消息。另一方面,知道如何处理消息,例如递交给某个特别队列、递交给所有队列、或是将消息丢弃。到底如何操作,取决于Exchange的类型。Exchange有常见以下3种类型:
- Fanout:广播,将消息交给所有绑定到交换机的队列
- Direct:定向,把消息交给符合指定routing key 的队列
- Topic:通配符,把消息交给符合routing pattern(路由模式) 的队列
Exchange(交换机)只负责转发消息,不具备存储消息的能力,因此如果没有任何队列与Exchange绑定,或者没有符合路由规则的队列,那么消息会丢失!
#3、 Publish/Subscribe发布与订阅模式(微博)
#( 1)模式说明
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hvbws0mk-1682215220914)(null)]
发布订阅模式: 1、每个消费者监听自己的队列。 2、生产者将消息发给broker,由交换机将消息转发到绑定此交换机的每个队列,每个绑定交换机的队列都将接收 到消息
#(2)代码
a、生产者
public class Producer {
//交换机名称
static final String FANOUT_EXCHAGE = "fanout_exchange";
//队列名称
static final String FANOUT_QUEUE_1 = "fanout_queue_1";
//队列名称
static final String FANOUT_QUEUE_2 = "fanout_queue_2";
public static void main(String[] args) throws IOException, TimeoutException {
//1.创建连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
//连接的ip
connectionFactory.setHost("localhost");
//连接的端口:
connectionFactory.setPort(5672);
//设置虚拟主机
connectionFactory.setVirtualHost("/");
//设置用户名:
connectionFactory.setUsername("cgboy");
connectionFactory.setPassword("cgboy");
//2.创建长连接
Connection connection = connectionFactory.newConnection();
//3.创建channel
Channel channel = connection.createChannel();
//声明队列
//String queue,队列名
//boolean durable,持久化
//boolean exclusive 排他的(当前队列使用,其他的队列就不能使用)
//boolean autoDelete 自动删除
//Map<String,Object> arguments 属性
channel.queueDeclare(FANOUT_QUEUE_1,true,false,false,null);
channel.queueDeclare(FANOUT_QUEUE_2,true,false,false,null);
// 声明交换机
// String exchange, 交换机名称
// BuiltinExchangeType type, 交换机类型
// boolean durable, 持久化
// boolean autoDelete, 自动删除
// Map<String, Object> arguments 属性
channel.exchangeDeclare(FANOUT_EXCHAGE, BuiltinExchangeType.FANOUT,true,false,null);
//队列绑定交换机
// String queue, 队列名称
// String exchange, 交换机名称
// String routingKey 路由键
channel.queueBind(FANOUT_QUEUE_1,FANOUT_EXCHAGE,"");
channel.queueBind(FANOUT_QUEUE_2,FANOUT_EXCHAGE,"");
//4.发消息
//String exchange 交换机
//String routingKey 路由键
//AMQP.BasicProperties props 属性
//byte[] body 消息
String msg = "hello rabbitMQ";
channel.basicPublish(FANOUT_EXCHAGE,"",null,msg.getBytes());
//5.关闭连接
channel.close();
connection.close();
}
}
b、消费者1监听队列1
public class Consumer1 {
static final String FANOUT_EXCHAGE = "fanout_exchange";
//队列名称
static final String FANOUT_QUEUE_1 = "fanout_queue_1";
//队列名称
static final String FANOUT_QUEUE_2 = "fanout_queue_2";
public static void main(String[] args) throws Exception {
//1创建连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
//主机地址;默认为 localhost
connectionFactory.setHost("localhost");
//连接端口;默认为 5672
connectionFactory.setPort(5672);
//虚拟主机名称;默认为 /
connectionFactory.setVirtualHost("/");
//连接用户名;默认为guest
connectionFactory.setUsername("cgboy");
//连接密码;默认为guest
connectionFactory.setPassword("cgboy");
//2创建连接
Connection connection = connectionFactory.newConnection();
//3创建频道
Channel channel = connection.createChannel();
//6声明(创建)队列
/**
* 参数1:队列名称
* 参数2:是否定义持久化队列
* 参数3:是否独占本次连接
* 参数4:是否在不使用的时候自动删除队列
* 参数5:队列其它参数
*/
//声明队列
//String queue, 队列名
// boolean durable, 持久化
// boolean exclusive, 排他的
// boolean autoDelete, 自动删除
// Map<String, Object> arguments 属性
channel.queueDeclare(FANOUT_QUEUE_1, true, false, false, null);
channel.queueDeclare(FANOUT_QUEUE_2, true, false, false, null);
// 声明交换机
// String exchange, 交换机名称
// BuiltinExchangeType type, 交换机类型
// boolean durable, 持久化
// boolean autoDelete, 自动删除
// Map<String, Object> arguments 属性
channel.exchangeDeclare(FANOUT_EXCHAGE, BuiltinExchangeType.FANOUT, true, false, null);
//队列绑定交换机
// String queue, 队列名称
// String exchange, 交换机名称
// String routingKey 路由键
channel.queueBind(FANOUT_QUEUE_1, FANOUT_EXCHAGE, "");
channel.queueBind(FANOUT_QUEUE_2, FANOUT_EXCHAGE, "");
//4监听某个队列
// String queue, 监听的队列名
// boolean autoAck, 是否自动应答
// Consumer callback 回调函数,收到消息,我要干啥
com.rabbitmq.client.Consumer consumer = new DefaultConsumer(channel) {
// 回调函数,收到消息,我要干啥
// String consumerTag, 消费者标签
// Envelope envelope, 信封 保存很多信息
// AMQP.BasicProperties properties, 属性
// byte[] body 消息字节数组
@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("DeliveryTag:"+envelope.getDeliveryTag()); //消息id
System.out.println(new String(body));
}
};
//消费者1监听队列1
channel.basicConsume(FANOUT_QUEUE_1, true, consumer);
//5 千万别关闭连接,要不然queue有了消息 推不过来了
// channel.close();
// connection.close();
}
}
c、消费者2监听队列2
public class Consumer2 {
static final String FANOUT_EXCHAGE = "fanout_exchange";
//队列名称
static final String FANOUT_QUEUE_1 = "fanout_queue_1";
//队列名称
static final String FANOUT_QUEUE_2 = "fanout_queue_2";
public static void main(String[] args) throws Exception {
//1创建连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
//主机地址;默认为 localhost
connectionFactory.setHost("localhost");
//连接端口;默认为 5672
connectionFactory.setPort(5672);
//虚拟主机名称;默认为 /
connectionFactory.setVirtualHost("/");
//连接用户名;默认为guest
connectionFactory.setUsername("cgboy");
//连接密码;默认为guest
connectionFactory.setPassword("cgboy");
//2创建连接
Connection connection = connectionFactory.newConnection();
//3创建频道
Channel channel = connection.createChannel();
//6声明(创建)队列
/**
* 参数1:队列名称
* 参数2:是否定义持久化队列
* 参数3:是否独占本次连接
* 参数4:是否在不使用的时候自动删除队列
* 参数5:队列其它参数
*/
//声明队列
//String queue, 队列名
// boolean durable, 持久化
// boolean exclusive, 排他的
// boolean autoDelete, 自动删除
// Map<String, Object> arguments 属性
channel.queueDeclare(FANOUT_QUEUE_1, true, false, false, null);
channel.queueDeclare(FANOUT_QUEUE_2, true, false, false, null);
// 声明交换机
// String exchange, 交换机名称
// BuiltinExchangeType type, 交换机类型
// boolean durable, 持久化
// boolean autoDelete, 自动删除
// Map<String, Object> arguments 属性
channel.exchangeDeclare(FANOUT_EXCHAGE, BuiltinExchangeType.FANOUT, true, false, null);
//队列绑定交换机
// String queue, 队列名称
// String exchange, 交换机名称
// String routingKey 路由键
channel.queueBind(FANOUT_QUEUE_1, FANOUT_EXCHAGE, "");
channel.queueBind(FANOUT_QUEUE_2, FANOUT_EXCHAGE, "");
//4监听某个队列
// String queue, 监听的队列名
// boolean autoAck, 是否自动应答
// Consumer callback 回调函数,收到消息,我要干啥
Consumer consumer = new DefaultConsumer(channel) {
// 回调函数,收到消息,我要干啥
// String consumerTag, 消费者标签
// Envelope envelope, 信封 保存很多信息
// AMQP.BasicProperties properties, 属性
// byte[] body 消息字节数组
@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("DeliveryTag:"+envelope.getDeliveryTag()); //消息id
System.out.println(new String(body));
}
};
channel.basicConsume(FANOUT_QUEUE_2, true, consumer);
//5 千万别关闭连接,要不然queue有了消息 推不过来了
// channel.close();
// connection.close();
}
}
运行生产者,生产者发送一条消息给broker,broker的交换机将该消息分发到各个队列,各个队列被消费者监听到后拿到消息:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kRYg21Qb-1682215218579)(C:\Users\陈刚\AppData\Roaming\Typora\typora-user-images\image-20230102163306738.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bUoWyiKy-1682215218579)(C:\Users\陈刚\AppData\Roaming\Typora\typora-user-images\image-20230102163412019.png)]
(4)小结
交换机需要与队列进行绑定,绑定之后;一个消息可以被多个消费者都收到。
发布订阅模式与工作队列模式的区别
1、工作队列模式不用定义交换机,而发布/订阅模式需要定义交换机。
2、发布/订阅模式的生产方是面向交换机发送消息,工作队列模式的生产方是面向队列发送消息(底层使用默认交换机)。
3、发布/订阅模式需要设置队列和交换机的绑定,工作队列模式不需要设置,实际上工作队列模式会将队列绑 定到默认的交换机 。
#4、Routing路由模式(分布式日志收集系统)
1)模式说明
路由模式特点:
- 队列与交换机的绑定,不能是任意绑定了,而是要指定一个
RoutingKey
(路由key) - 消息的发送方在 向 Exchange发送消息时,也必须指定消息的
RoutingKey
。 - Exchange不再把消息交给每一个绑定的队列,而是根据消息的
Routing Key
进行判断,只有队列的Routingkey
与消息的Routing key
完全一致,才会接收到消息
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FzozKBMk-1682215221044)(null)]
图解:
- P:生产者,向Exchange发送消息,发送消息时,会指定一个routing key。
- X:Exchange(交换机),接收生产者的消息,然后把消息递交给 与routing key完全匹配的队列
- C1:消费者,其所在队列指定了需要routing key 为 error 的消息
- C2:消费者,其所在队列指定了需要routing key 为 info、error、warning 的消息
#(2)代码
在编码上与 Publish/Subscribe发布与订阅模式
的区别是交换机的类型为:Direct,还有队列绑定交换机的时候需要指定routing key。
a、生产者
public class Producer {
//交换机名称
static final String DIRECT_EXCHAGE = "direct_exchange";
//队列名称
static final String FANOUT_QUEUE_1 = "fanout_queue_1";
//队列名称
static final String FANOUT_QUEUE_2 = "fanout_queue_2";
public static void main(String[] args) throws IOException, TimeoutException {
//1.创建连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
//连接的ip
connectionFactory.setHost("localhost");
//连接的端口:
connectionFactory.setPort(5672);
//设置虚拟主机
connectionFactory.setVirtualHost("/");
//设置用户名:
connectionFactory.setUsername("cgboy");
connectionFactory.setPassword("cgboy");
//2.创建长连接
Connection connection = connectionFactory.newConnection();
//3.创建channel
Channel channel = connection.createChannel();
//声明队列
//String queue,队列名
//boolean durable,持久化
//boolean exclusive 排他的(当前队列使用,其他的队列就不能使用)
//boolean autoDelete 自动删除
//Map<String,Object> arguments 属性
channel.queueDeclare(FANOUT_QUEUE_1,true,false,false,null);
channel.queueDeclare(FANOUT_QUEUE_2,true,false,false,null);
// 声明交换机
// String exchange, 交换机名称
// BuiltinExchangeType type, 交换机类型
// boolean durable, 持久化
// boolean autoDelete, 自动删除
// Map<String, Object> arguments 属性
channel.exchangeDeclare(DIRECT_EXCHAGE, BuiltinExchangeType.DIRECT,true,false,null);
//队列绑定交换机
// String queue, 队列名称
// String exchange, 交换机名称
// String routingKey 路由键
channel.queueBind(FANOUT_QUEUE_1,DIRECT_EXCHAGE,"error");
channel.queueBind(FANOUT_QUEUE_2,DIRECT_EXCHAGE,"info");
channel.queueBind(FANOUT_QUEUE_2,DIRECT_EXCHAGE,"error");
channel.queueBind(FANOUT_QUEUE_2,DIRECT_EXCHAGE,"warning");
//4.发消息
//String exchange 交换机
//String routingKey 路由键
//AMQP.BasicProperties props 属性
//byte[] body 消息
String msg="hello rabbitmq!routing error";
channel.basicPublish(DIRECT_EXCHAGE,"error",null,msg.getBytes());
String msg1="hello rabbitmq!routing info";
channel.basicPublish(DIRECT_EXCHAGE,"info",null,msg1.getBytes());
String msg2="hello rabbitmq!routing warning";
channel.basicPublish(DIRECT_EXCHAGE,"warning",null,msg2.getBytes());
//5.关闭连接
channel.close();
connection.close();
}
}
b、消费者1
package com.ydlclass.rabbitmq.routing;
import com.rabbitmq.client.*;
import java.io.IOException;
/**
* @Created by IT李老师
* 公主号 “元动力课堂”
* 个人微 itlils
*/
public class Consumer1 {
//交换机名称
static final String DIRECT_EXCHAGE = "direct_exchange";
//队列名称
static final String DIRECT_QUEUE_1 = "direct_queue_1";
//队列名称
static final String DIRECT_QUEUE_2 = "direct_queue_2";
public static void main(String[] args) throws Exception {
//1创建连接工厂
ConnectionFactory connectionFactory=new ConnectionFactory();
//连接的ip
connectionFactory.setHost("localhost");
//连接的端口
connectionFactory.setPort(5672);
//设置虚拟主机
connectionFactory.setVirtualHost("/");
//设置用户名
connectionFactory.setUsername("itlils");
//设置密码
connectionFactory.setPassword("itlils");
//2创建长连接
Connection connection = connectionFactory.newConnection();
//3创建channel
Channel channel = connection.createChannel();
//声明队列
//String queue, 队列名
// boolean durable, 持久化
// boolean exclusive, 排他的
// boolean autoDelete, 自动删除
// Map<String, Object> arguments 属性
channel.queueDeclare(DIRECT_QUEUE_1,true,false,false,null);
channel.queueDeclare(DIRECT_QUEUE_2,true,false,false,null);
// 声明交换机
// String exchange, 交换机名称
// BuiltinExchangeType type, 交换机类型
// boolean durable, 持久化
// boolean autoDelete, 自动删除
// Map<String, Object> arguments 属性
channel.exchangeDeclare(DIRECT_EXCHAGE, BuiltinExchangeType.DIRECT,true,false,null);
//队列绑定交换机
// String queue, 队列名称
// String exchange, 交换机名称
// String routingKey 路由键
channel.queueBind(DIRECT_QUEUE_1,DIRECT_EXCHAGE,"error");
channel.queueBind(DIRECT_QUEUE_2,DIRECT_EXCHAGE,"info");
channel.queueBind(DIRECT_QUEUE_2,DIRECT_EXCHAGE,"error");
channel.queueBind(DIRECT_QUEUE_2,DIRECT_EXCHAGE,"warning");
//4监听某个队列
// String queue, 监听的队列名
// boolean autoAck, 是否自动应答
// Consumer callback 回调函数,收到消息,我要干啥
com.rabbitmq.client.Consumer consumer=new DefaultConsumer(channel){
// 回调函数,收到消息,我要干啥
// String consumerTag, 消费者标签
// Envelope envelope, 信封 保存很多信息
// AMQP.BasicProperties properties, 属性
// byte[] body 消息字节数组
@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("DeliveryTag:"+envelope.getDeliveryTag()); //消息id
System.out.println(new String(body));
}
};
channel.basicConsume(DIRECT_QUEUE_1,true,consumer);
//5 千万别关闭连接,要不然queue有了消息 推不过来了
// channel.close();
// connection.close();
}
}
#c、消费者2
package com.ydlclass.rabbitmq.routing;
import com.rabbitmq.client.*;
import java.io.IOException;
/**
* @Created by IT李老师
* 公主号 “元动力课堂”
* 个人微 itlils
*/
public class Consumer2 {
//交换机名称
static final String DIRECT_EXCHAGE = "direct_exchange";
//队列名称
static final String DIRECT_QUEUE_1 = "direct_queue_1";
//队列名称
static final String DIRECT_QUEUE_2 = "direct_queue_2";
public static void main(String[] args) throws Exception {
//1创建连接工厂
ConnectionFactory connectionFactory=new ConnectionFactory();
//连接的ip
connectionFactory.setHost("localhost");
//连接的端口
connectionFactory.setPort(5672);
//设置虚拟主机
connectionFactory.setVirtualHost("/");
//设置用户名
connectionFactory.setUsername("itlils");
//设置密码
connectionFactory.setPassword("itlils");
//2创建长连接
Connection connection = connectionFactory.newConnection();
//3创建channel
Channel channel = connection.createChannel();
//声明队列
//String queue, 队列名
// boolean durable, 持久化
// boolean exclusive, 排他的
// boolean autoDelete, 自动删除
// Map<String, Object> arguments 属性
channel.queueDeclare(DIRECT_QUEUE_1,true,false,false,null);
channel.queueDeclare(DIRECT_QUEUE_2,true,false,false,null);
// 声明交换机
// String exchange, 交换机名称
// BuiltinExchangeType type, 交换机类型
// boolean durable, 持久化
// boolean autoDelete, 自动删除
// Map<String, Object> arguments 属性
channel.exchangeDeclare(DIRECT_EXCHAGE, BuiltinExchangeType.DIRECT,true,false,null);
//队列绑定交换机
// String queue, 队列名称
// String exchange, 交换机名称
// String routingKey 路由键
channel.queueBind(DIRECT_QUEUE_1,DIRECT_EXCHAGE,"error");
channel.queueBind(DIRECT_QUEUE_2,DIRECT_EXCHAGE,"info");
channel.queueBind(DIRECT_QUEUE_2,DIRECT_EXCHAGE,"error");
channel.queueBind(DIRECT_QUEUE_2,DIRECT_EXCHAGE,"warning");
//4监听某个队列
// String queue, 监听的队列名
// boolean autoAck, 是否自动应答
// Consumer callback 回调函数,收到消息,我要干啥
Consumer consumer=new DefaultConsumer(channel){
// 回调函数,收到消息,我要干啥
// String consumerTag, 消费者标签
// Envelope envelope, 信封 保存很多信息
// AMQP.BasicProperties properties, 属性
// byte[] body 消息字节数组
@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("DeliveryTag:"+envelope.getDeliveryTag()); //消息id
System.out.println(new String(body));
}
};
channel.basicConsume(DIRECT_QUEUE_2,true,consumer);
//5 千万别关闭连接,要不然queue有了消息 推不过来了
// channel.close();
// connection.close();
}
}
运行生产者后,broker拿到消息分发给队列,监听的消费者拿到队列的信息打印输出,消费者1只能拿到error的信息,消费者2能拿到全部类型信息:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Df7uFTFI-1682215218579)(C:\Users\陈刚\AppData\Roaming\Typora\typora-user-images\image-20230102173206290.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eN6QH1DC-1682215218579)(C:\Users\陈刚\AppData\Roaming\Typora\typora-user-images\image-20230102173215200.png)]
(4)小结
Routing模式要求队列在绑定交换机时要指定routing key,消息会转发到符合routing key的队列。
#5、 Topics通配符模式
#(1) 模式说明
ydlclass.taiyuan.caiwubu.info "本月工资大家涨两千!"
ydlclass.taiyuan.renshi.error "李老师携款潜逃!"
ydlclass.beijing.caiwubu.error "因为李老师逃了,全国所有校区降薪两千。不行就毕业!"
ydlclass.lasa.caiwubu.info "lasa校区成立了!"
ydlclass.taiyuan.shitangbu.info "太原校区学生吃饭免费!"
Topic
类型与Direct
相比,都是可以根据RoutingKey
把消息路由到不同的队列。只不过Topic
类型Exchange
可以让队列在绑定Routing key
的时候使用通配符!
Routingkey` 一般都是有一个或多个单词组成,多个单词之间以”.”分割,例如: `item.insert
通配符规则:
#
:匹配一个或多个词
*
:匹配不多不少恰好1个词
举例:
我是太原校区校长 ydlclass.taiyuan.*.*
itlils 我是总部财务主管 ydlclass.*.caiwubu.*
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PmvxUNr8-1682215221120)(null)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ECav144X-1682215221219)(null)]
图解:
- 红色Queue:绑定的是
usa.#
,因此凡是以usa.
开头的routing key
都会被匹配到 - 黄色Queue:绑定的是
#.news
,因此凡是以.news
结尾的routing key
都会被匹配
#(2) 代码
a、生产者
package com.ydlclass.rabbitmq.topic;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
/**
* @Created by IT李老师
* 公主号 “元动力课堂”
* 个人微 itlils
*/
public class Producer {
//交换机名称
static final String TOPIC_EXCHAGE = "topic_exchange";
//队列名称
static final String TOPIC_QUEUE_1 = "topic_queue_1";
//队列名称
static final String TOPIC_QUEUE_2 = "topic_queue_2";
public static void main(String[] args) throws Exception {
//1创建连接工厂
ConnectionFactory connectionFactory=new ConnectionFactory();
//连接的ip
connectionFactory.setHost("localhost");
//连接的端口
connectionFactory.setPort(5672);
//设置虚拟主机
connectionFactory.setVirtualHost("/");
//设置用户名
connectionFactory.setUsername("itlils");
//设置密码
connectionFactory.setPassword("itlils");
//2创建长连接
Connection connection = connectionFactory.newConnection();
//3创建channel
Channel channel = connection.createChannel();
//声明队列
//String queue, 队列名
// boolean durable, 持久化
// boolean exclusive, 排他的
// boolean autoDelete, 自动删除
// Map<String, Object> arguments 属性
channel.queueDeclare(TOPIC_QUEUE_1,true,false,false,null);
channel.queueDeclare(TOPIC_QUEUE_2,true,false,false,null);
// 声明交换机
// String exchange, 交换机名称
// BuiltinExchangeType type, 交换机类型
// boolean durable, 持久化
// boolean autoDelete, 自动删除
// Map<String, Object> arguments 属性
channel.exchangeDeclare(TOPIC_EXCHAGE, BuiltinExchangeType.TOPIC,true,false,null);
//队列绑定交换机
// String queue, 队列名称
// String exchange, 交换机名称
// String routingKey 路由键
channel.queueBind(TOPIC_QUEUE_1,TOPIC_EXCHAGE,"ydlclass.taiyuan.*.*"); //我是太原校区校长的队列
channel.queueBind(TOPIC_QUEUE_2,TOPIC_EXCHAGE,"ydlclass.*.caiwubu.*");//我是总部财务主管的队列
//4发消息
// String exchange, 交换机
// String routingKey, 路由键
// AMQP.BasicProperties props, 属性
// byte[] body 消息 string byte[] char[]如何相互转换的?
String msg1="hello rabbitmq!topic 本月工资大家涨两千!";
channel.basicPublish(TOPIC_EXCHAGE,"ydlclass.taiyuan.caiwubu.info",null,msg1.getBytes());
String msg2="hello rabbitmq!topic 李老师携款潜逃!";
channel.basicPublish(TOPIC_EXCHAGE,"ydlclass.taiyuan.renshi.error",null,msg2.getBytes());
String msg3="hello rabbitmq!topic 因为李老师逃了,全国所有校区降薪两千。不行就毕业!";
channel.basicPublish(TOPIC_EXCHAGE,"ydlclass.beijing.caiwubu.error",null,msg3.getBytes());
//5关闭连接 资源关闭的顺序,先关后出来的资源,最后关,第一个资源
channel.close();
connection.close();
}
}
#b、消费者1
接收两种类型的消息:更新商品和删除商品
package com.ydlclass.rabbitmq.topic;
import com.rabbitmq.client.*;
import java.io.IOException;
/**
* @Created by IT李老师
* 公主号 “元动力课堂”
* 个人微 itlils
*/
public class Consumer1 {
//交换机名称
static final String TOPIC_EXCHAGE = "topic_exchange";
//队列名称
static final String TOPIC_QUEUE_1 = "topic_queue_1";
//队列名称
static final String TOPIC_QUEUE_2 = "topic_queue_2";
public static void main(String[] args) throws Exception {
//1创建连接工厂
ConnectionFactory connectionFactory=new ConnectionFactory();
//连接的ip
connectionFactory.setHost("localhost");
//连接的端口
connectionFactory.setPort(5672);
//设置虚拟主机
connectionFactory.setVirtualHost("/");
//设置用户名
connectionFactory.setUsername("itlils");
//设置密码
connectionFactory.setPassword("itlils");
//2创建长连接
Connection connection = connectionFactory.newConnection();
//3创建channel
Channel channel = connection.createChannel();
//声明队列
//String queue, 队列名
// boolean durable, 持久化
// boolean exclusive, 排他的
// boolean autoDelete, 自动删除
// Map<String, Object> arguments 属性
channel.queueDeclare(TOPIC_QUEUE_1,true,false,false,null);
channel.queueDeclare(TOPIC_QUEUE_2,true,false,false,null);
// 声明交换机
// String exchange, 交换机名称
// BuiltinExchangeType type, 交换机类型
// boolean durable, 持久化
// boolean autoDelete, 自动删除
// Map<String, Object> arguments 属性
channel.exchangeDeclare(TOPIC_EXCHAGE, BuiltinExchangeType.TOPIC,true,false,null);
//队列绑定交换机
// String queue, 队列名称
// String exchange, 交换机名称
// String routingKey 路由键
channel.queueBind(TOPIC_QUEUE_1,TOPIC_EXCHAGE,"ydlclass.taiyuan.*.*"); //我是太原校区校长的队列
channel.queueBind(TOPIC_QUEUE_2,TOPIC_EXCHAGE,"ydlclass.*.caiwubu.*");//我是总部财务主管的队列
//4监听某个队列
// String queue, 监听的队列名
// boolean autoAck, 是否自动应答
// Consumer callback 回调函数,收到消息,我要干啥
Consumer consumer=new DefaultConsumer(channel){
// 回调函数,收到消息,我要干啥
// String consumerTag, 消费者标签
// Envelope envelope, 信封 保存很多信息
// AMQP.BasicProperties properties, 属性
// byte[] body 消息字节数组
@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("DeliveryTag:"+envelope.getDeliveryTag()); //消息id
System.out.println(new String(body));
}
};
channel.basicConsume(TOPIC_QUEUE_1,true,consumer);
//5 千万别关闭连接,要不然queue有了消息 推不过来了
// channel.close();
// connection.close();
}
}
#c、消费者2
接收所有类型的消息:新增商品,更新商品和删除商品。
package com.ydlclass.rabbitmq.topic;
import com.rabbitmq.client.*;
import java.io.IOException;
/**
* @Created by IT李老师
* 公主号 “元动力课堂”
* 个人微 itlils
*/
public class Consumer2 {
//交换机名称
static final String TOPIC_EXCHAGE = "topic_exchange";
//队列名称
static final String TOPIC_QUEUE_1 = "topic_queue_1";
//队列名称
static final String TOPIC_QUEUE_2 = "topic_queue_2";
public static void main(String[] args) throws Exception {
//1创建连接工厂
ConnectionFactory connectionFactory=new ConnectionFactory();
//连接的ip
connectionFactory.setHost("localhost");
//连接的端口
connectionFactory.setPort(5672);
//设置虚拟主机
connectionFactory.setVirtualHost("/");
//设置用户名
connectionFactory.setUsername("itlils");
//设置密码
connectionFactory.setPassword("itlils");
//2创建长连接
Connection connection = connectionFactory.newConnection();
//3创建channel
Channel channel = connection.createChannel();
//声明队列
//String queue, 队列名
// boolean durable, 持久化
// boolean exclusive, 排他的
// boolean autoDelete, 自动删除
// Map<String, Object> arguments 属性
channel.queueDeclare(TOPIC_QUEUE_1,true,false,false,null);
channel.queueDeclare(TOPIC_QUEUE_2,true,false,false,null);
// 声明交换机
// String exchange, 交换机名称
// BuiltinExchangeType type, 交换机类型
// boolean durable, 持久化
// boolean autoDelete, 自动删除
// Map<String, Object> arguments 属性
channel.exchangeDeclare(TOPIC_EXCHAGE, BuiltinExchangeType.TOPIC,true,false,null);
//队列绑定交换机
// String queue, 队列名称
// String exchange, 交换机名称
// String routingKey 路由键
channel.queueBind(TOPIC_QUEUE_1,TOPIC_EXCHAGE,"ydlclass.taiyuan.*.*"); //我是太原校区校长的队列
channel.queueBind(TOPIC_QUEUE_2,TOPIC_EXCHAGE,"ydlclass.*.caiwubu.*");//我是总部财务主管的队列
//4监听某个队列
// String queue, 监听的队列名
// boolean autoAck, 是否自动应答
// Consumer callback 回调函数,收到消息,我要干啥
Consumer consumer=new DefaultConsumer(channel){
// 回调函数,收到消息,我要干啥
// String consumerTag, 消费者标签
// Envelope envelope, 信封 保存很多信息
// AMQP.BasicProperties properties, 属性
// byte[] body 消息字节数组
@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("DeliveryTag:"+envelope.getDeliveryTag()); //消息id
System.out.println(new String(body));
}
};
channel.basicConsume(TOPIC_QUEUE_2,true,consumer);
//5 千万别关闭连接,要不然queue有了消息 推不过来了
// channel.close();
// connection.close();
}
}
测试:
消费者1 接受2条消息
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qzLOUWaw-1682215219936)(null)]
消费者2 接受2条消息
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TWVYwoAv-1682215221275)(null)]
#(4)小结
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 整合RabbitMQ-了解
#1、搭建生产者工程
#(1)创建工程
spring-rabbitmq-producer
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2kiJXnx4-1682215221377)(null)]
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.ydlclass</groupId>
<artifactId>spring-rabbitmq-producer</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<version>2.1.8.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.7.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
#(3) 配置整合
- 创建
resources\rabbitmq.properties
连接参数等配置文件;
rabbitmq.host=localhost
rabbitmq.port=5672
rabbitmq.username=cgboy
rabbitmq.password=cgboy
rabbitmq.virtual-host=/
- 创建
resources\spring-rabbitmq.xml
整合配置文件;
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">
<!--加载配置文件-->
<context:property-placeholder location="classpath:rabbitmq.properties"/>
<!-- 定义rabbitmq connectionFactory -->
<rabbit:connection-factory id="connectionFactory"
host="${rabbitmq.host}"
port="${rabbitmq.port}"
username="${rabbitmq.username}"
password="${rabbitmq.password}"
virtual-host="${rabbitmq.virtual-host}"/>
<!--定义管理交换机、队列-->
<rabbit:admin connection-factory="connectionFactory"/>
<!--定义持久化队列,不存在则自动创建;不绑定到交换机则绑定到默认交换机
默认交换机类型为direct,名字为:"",路由键为队列的名称
-->
<rabbit:queue id="ydlqueue" name="ydlqueue" auto-declare="true"/>
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~广播;所有队列都能收到消息~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<!--定义广播交换机中的持久化队列,不存在则自动创建-->
<rabbit:queue id="spring_fanout_queue_1" name="spring_fanout_queue_1" auto-declare="true" auto-delete="false" durable="true"/>
<!--定义广播交换机中的持久化队列,不存在则自动创建-->
<rabbit:queue id="spring_fanout_queue_2" name="spring_fanout_queue_2" auto-declare="true"/>
<!--定义广播类型交换机;并绑定上述两个队列-->
<rabbit:fanout-exchange id="spring_fanout_exchange" name="spring_fanout_exchange" auto-declare="true">
<rabbit:bindings>
<rabbit:binding queue="spring_fanout_queue_1"/>
<rabbit:binding queue="spring_fanout_queue_2"/>
</rabbit:bindings>
</rabbit:fanout-exchange>
<!-- routing 模式-->
<rabbit:queue id="spring_direct_queue_1" name="spring_direct_queue_1" auto-declare="true"/>
<rabbit:queue id="spring_direct_queue_2" name="spring_direct_queue_2" auto-declare="true"/>
<rabbit:direct-exchange id="spring_direct_exchange" name="spring_direct_exchange" auto-declare="true">
<rabbit:bindings>
<rabbit:binding queue="spring_direct_queue_1" key="error"></rabbit:binding>
<rabbit:binding queue="spring_direct_queue_2" key="info"></rabbit:binding>
<rabbit:binding queue="spring_direct_queue_2" key="error"></rabbit:binding>
<rabbit:binding queue="spring_direct_queue_2" key="warning"></rabbit:binding>
</rabbit:bindings>
</rabbit:direct-exchange>
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~通配符;*匹配一个单词,#匹配多个单词 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<rabbit:queue id="spring_topic_queue_1" name="spring_topic_queue_1" auto-declare="true"/>
<rabbit:queue id="spring_topic_queue_2" name="spring_topic_queue_2" auto-declare="true"/>
<rabbit:topic-exchange id="spring_topic_exchange" name="spring_topic_exchange">
<rabbit:bindings>
<rabbit:binding queue="spring_topic_queue_1" pattern="ydlclass.taiyuan.*.*"></rabbit:binding>
<rabbit:binding queue="spring_topic_queue_2" pattern="ydlclass.*.caiwubu.*"></rabbit:binding>
</rabbit:bindings>
</rabbit:topic-exchange>
<!--定义rabbitTemplate对象操作可以在代码中方便发送消息-->
<rabbit:template id="rabbitTemplate" connection-factory="connectionFactory"/>
</beans>
首先测试简单工作模式:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-rabbitmq.xml")
public class RabbitMqTest {
@Autowired
RabbitTemplate rabbitTemplate;
//简单模式
//简单模式发送
@Test
public void helloTest(){
// String exchange, 交换机
// String routingKey, 路由键
// Object message Object消息体 User car lunchuan
String msg="hello rabbitmq!";
rabbitTemplate.convertAndSend("","ydlqueue",msg);
}
}
运行后,我们打开rabbitmq,发现ydlqueue队列,并查看到了消息:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qTN8jEGx-1682215218581)(C:\Users\陈刚\AppData\Roaming\Typora\typora-user-images\image-20230102224426097.png)]
测试发布订阅模式:
xml中,定义交换机,队列和交换机绑定队列:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QeGzhuc6-1682215218581)(C:\Users\陈刚\AppData\Roaming\Typora\typora-user-images\image-20230102224618200.png)]
测试:
//测试发布订阅模式
@Test
public void publishTest(){
String msg="hello rabbitmq! publish";
rabbitTemplate.convertAndSend("spring_fanout_exchange","",msg);
}
运行后,我们打开rabbitmq找到了该交换机和它绑定的两个队列,也能在队列中找到发送的msg内容:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hjn5PpiB-1682215218581)(C:\Users\陈刚\AppData\Roaming\Typora\typora-user-images\image-20230102225357979.png)]
测试spring整合routing模式:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LicC3Wtl-1682215218581)(C:\Users\陈刚\AppData\Roaming\Typora\typora-user-images\image-20230103162711106.png)]
代码:
//测试routing模式
@Test
public void routingTest(){
String msg="hello rabbitmq!routing error";
rabbitTemplate.convertAndSend("spring_direct_exchange","error",msg);
String msg1="hello rabbitmq!routing info";
rabbitTemplate.convertAndSend("spring_direct_exchange","info",msg1);
String msg2="hello rabbitmq!routing warning";
rabbitTemplate.convertAndSend("spring_direct_exchange","warning",msg2);
}
运行后,在rabbitmq中能看到对应交换机和绑定的队列:且也能拿到对应消息
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HLOUbBTD-1682215218581)(C:\Users\陈刚\AppData\Roaming\Typora\typora-user-images\image-20230103162820776.png)]
通配符(topic)模式:
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~通配符;*匹配一个单词,#匹配多个单词 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<rabbit:queue id="spring_topic_queue_1" name="spring_topic_queue_1" auto-declare="true"/>
<rabbit:queue id="spring_topic_queue_2" name="spring_topic_queue_2" auto-declare="true"/>
<rabbit:topic-exchange id="spring_topic_exchange" name="spring_topic_exchange">
<rabbit:bindings>
<rabbit:binding queue="spring_topic_queue_1" pattern="ydlclass.taiyuan.*.*"></rabbit:binding>
<rabbit:binding queue="spring_topic_queue_2" pattern="ydlclass.*.caiwubu.*"></rabbit:binding>
</rabbit:bindings>
</rabbit:topic-exchange>
<!--定义rabbitTemplate对象操作可以在代码中方便发送消息-->
<rabbit:template id="rabbitTemplate" connection-factory="connectionFactory"/>
代码:
//测试topic模式
@Test
public void topicTest(){
String msg1="hello rabbitmq!topic 本月工资大家涨两千!";
rabbitTemplate.convertAndSend("spring_topic_exchange","ydlclass.taiyuan.caiwubu.info",msg1);
String msg2="hello rabbitmq!topic 李老师携款潜逃!";
rabbitTemplate.convertAndSend("spring_topic_exchange","ydlclass.taiyuan.renshi.error",msg2);
String msg3="hello rabbitmq!topic 因为李老师逃了,全国所有校区降薪两千。不行就毕业!";
rabbitTemplate.convertAndSend("spring_topic_exchange","ydlclass.beijing.caiwubu.error",msg3);
}
运行后,自动匹配到对应的队列,拿到对应的消息
2、搭建消费者工程
#(1)创建工程
spring-rabbitmq-consumer
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jNOOyPim-1682215218581)(C:\Users\陈刚\AppData\Roaming\Typora\typora-user-images\image-20230103171444671.png)]
(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.ydlclass</groupId>
<artifactId>spring-rabbitmq-consumer</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<version>2.1.8.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.7.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
#(3) 配置整合
- 创建
resources\rabbitmq.properties
连接参数等配置文件;
rabbitmq.host=localhost
rabbitmq.port=5672
rabbitmq.username=itlils
rabbitmq.password=itlils
rabbitmq.virtual-host=/
- 创建 resources\spring-rabbitmq.xml` 整合配置文件;
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">
<!--加载配置文件-->
<context:property-placeholder location="classpath:rabbitmq.properties"/>
<!-- 定义rabbitmq connectionFactory -->
<rabbit:connection-factory id="connectionFactory"
host="${rabbitmq.host}"
port="${rabbitmq.port}"
username="${rabbitmq.username}"
password="${rabbitmq.password}"
virtual-host="${rabbitmq.virtual-host}"/>
<bean id="springQueueListener" class="com.ydlclass.rabbitmq.listener.SpringQueueListener"/>
<!-- <bean id="fanoutListener1" class="com.ydlclass.rabbitmq.listener.FanoutListener1"/>-->
<!-- <bean id="fanoutListener2" class="com.ydlclass.rabbitmq.listener.FanoutListener2"/>-->
<!-- <bean id="topicListenerStar" class="com.ydlclass.rabbitmq.listener.TopicListenerStar"/>-->
<!-- <bean id="topicListenerWell" class="com.ydlclass.rabbitmq.listener.TopicListenerWell"/>-->
<!-- <bean id="topicListenerWell2" class="com.ydlclass.rabbitmq.listener.TopicListenerWell2"/>-->
<rabbit:listener-container connection-factory="connectionFactory" auto-declare="true">
<rabbit:listener ref="springQueueListener" queue-names="ydlqueue"/>
<!-- <rabbit:listener ref="fanoutListener1" queue-names="spring_fanout_queue_1"/>-->
<!-- <rabbit:listener ref="fanoutListener2" queue-names="spring_fanout_queue_2"/>-->
<!-- <rabbit:listener ref="topicListenerStar" queue-names="spring_topic_queue_star"/>-->
<!-- <rabbit:listener ref="topicListenerWell" queue-names="spring_topic_queue_well"/>-->
<!-- <rabbit:listener ref="topicListenerWell2" queue-names="spring_topic_queue_well2"/>-->
</rabbit:listener-container>
</beans>
#(4) 消息监听器
#a、队列监听器
创建 spring-rabbitmq-consumer\src\main\java\com\ydlclass\rabbitmq\listener\SpringQueueListener.java
package com.ydlclass.rabbitmq.listener;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;
/**
* @Created by IT李老师
* 公主号 “元动力课堂”
* 个人微 itlils
*/
//缺啥补啥 干就完事
public class SpringQueueListener implements MessageListener {
//有了消息 做什么
@Override
public void onMessage(Message message) {
//业务逻辑
byte[] body = message.getBody();
System.out.println(new String(body));
}
}
为了让spring容器一直运行,我们需要在测试类中,启动spring容器:com.ydlclass.rabbimq
package com.ydlclass.rabbitmq;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @Created by IT李老师
* 公主号 “元动力课堂”
* 个人微 itlils
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-rabbitmq.xml")
public class ConsumerTest {
@Test
public void consumerTest(){
while (true){
}
}
}
#b、广播监听器1
创建 spring-rabbitmq-consumer\src\main\java\com\ydlclass\rabbitmq\listener\FanoutListener1.java
#c、广播监听器2
创建 spring-rabbitmq-consumer\src\main\java\com\ydlclass\rabbitmq\listener\FanoutListener2.java
#d、星号通配符监听器
创建 spring-rabbitmq-consumer\src\main\java\com\ydlclass\rabbitmq\listener\TopicListenerStar.java
#e、井号通配符监听器
创建 spring-rabbitmq-consumer\src\main\java\com\ydlclass\rabbitmq\listener\TopicListenerWell.java
#f、井号通配符监听器2
创建 spring-rabbitmq-consumer\src\main\java\com\ydlclass\rabbitmq\listener\TopicListenerWell2.java
测试:我们在前面设置的生产者工程中发送了一个简单工作模式的消息:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RKGYeqet-1682215218582)(C:\Users\陈刚\AppData\Roaming\Typora\typora-user-images\image-20230103171622094.png)]
消费者这边立刻就监听到显示了出来:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W3QTuZYz-1682215218582)(C:\Users\陈刚\AppData\Roaming\Typora\typora-user-images\image-20230103171707496.png)]
第七章 Spring Boot整合RabbitMQ
1 .搭建生产者
引入依赖:
<?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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.11.RELEASE</version>
<relativePath />
</parent>
<groupId>com.cgboy</groupId>
<artifactId>springboot-rabbitmq-producer</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<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>
主启动类:
@SpringBootApplication
public class ProducerApp {
public static void main(String[] args) {
SpringApplication.run(ProducerApp.class,args);
}
}
配置文件:
spring:
rabbitmq:
host: localhost
port: 5672
virtual-host: /
username: cgboy
password: cgboy
简单模式:
配置类:
@Configuration
public class RabbitMQConfig {
@Bean("boot_hello_queue")
public Queue queue() {
//String queue, 队列名
// boolean durable, 持久化
// boolean exclusive, 排他的
// boolean autoDelete, 自动删除
// Map<String, Object> arguments 属性
return new Queue("boot_hello_queue", true, false, false, null);
}
}
测试类:
@SpringBootTest(classes = ProducerApp.class)
@RunWith(SpringRunner.class)
public class ProducerTest {
@Autowired
RabbitTemplate rabbitTemplate;
@Test
public void helloTest(){
String msg="hello rabbitmq!";
rabbitTemplate.convertAndSend("","boot_hello_queue",msg);
}
}
运行后哦,在rabbitmq成功拿到对应队列和消息:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iSzcHhfO-1682215218582)(C:\Users\陈刚\AppData\Roaming\Typora\typora-user-images\image-20230103181049272.png)]
订阅模式:
配置类中定义交换机,队列,并交换机绑定队列:
//发布订阅模式
//交换机名称
public static final String FANOUT_EXCHAGE = "boot_fanout_exchange";
//队列名称
public static final String FANOUT_QUEUE_1 = "boot_fanout_queue_1";
//队列名称
public static final String FANOUT_QUEUE_2 = "boot_fanout_queue_2";
@Bean(FANOUT_QUEUE_1)
public Queue FANOUT_QUEUE_1(){
return new Queue(FANOUT_QUEUE_1,true,false,false,null);
}
@Bean(FANOUT_QUEUE_2)
public Queue FANOUT_QUEUE_2(){
return new Queue(FANOUT_QUEUE_2,true,false,false,null);
}
@Bean(FANOUT_EXCHAGE)
public Exchange FANOUT_EXCHAGE(){
return ExchangeBuilder.fanoutExchange(FANOUT_EXCHAGE).durable(true).build();
}
@Bean
public Binding FANOUT_QUEUE_1_FANOUT_EXCHAGE(@Qualifier(FANOUT_QUEUE_1) Queue queue,
@Qualifier(FANOUT_EXCHAGE) Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with("").noargs();
}
@Bean
public Binding FANOUT_QUEUE_2_FANOUT_EXCHAGE(@Qualifier(FANOUT_QUEUE_2) Queue queue,
@Qualifier(FANOUT_EXCHAGE) Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with("").noargs();
}
测试:
@Test
public void publishTest(){
String msg="hello rabbitmq!publishTest";
rabbitTemplate.convertAndSend(RabbitMQConfig.FANOUT_EXCHAGE,"",msg);
}
运行后擦好看rabbitmq找到对应交换机和队列:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DhDijmnV-1682215218582)(C:\Users\陈刚\AppData\Roaming\Typora\typora-user-images\image-20230103182939949.png)]
3、搭建消费者工程
#(1) 创建工程
创建消费者工程springboot-rabbitmq-consumer
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-McX3H4O7-1682215221565)(null)]
#(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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
</parent>
<groupId>com.ydlclass</groupId>
<artifactId>springboot-rabbitmq-consumer</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
</dependencies>
</project>
#(3) 启动类
package com.ydlclass.rabbitmq;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @Created by IT李老师
* 公主号 “元动力课堂”
* 个人微 itlils
*/
@SpringBootApplication
public class ConsumerApp {
public static void main(String[] args) {
SpringApplication.run(ConsumerApp.class,args);
}
}
#(4) 配置RabbitMQ
创建application.yml,内容如下:
spring:
rabbitmq:
host: localhost
port: 5672
virtual-host: /
username: itlils
password: itlils
#(5) 消息监听处理类
编写消息监听器com.ydlclass.rabbitmq.listener.MyListener
package com.ydlclass.rabbitmq.listener;
import com.ydlclass.rabbitmq.config.RabbitConfig;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
/**
* @Created by IT李老师
* 公主号 “元动力课堂”
* 个人微 itlils
*/
@Component
public class MyListener {
@RabbitListener(queues = RabbitConfig.FANOUT_QUEUE_1)
public void receiveMsg(Message message){
//业务逻辑
byte[] body = message.getBody();
System.out.println(new String(body));
MessageProperties messageProperties = message.getMessageProperties(); //参数
System.out.println(messageProperties.getMessageId());
System.out.println(messageProperties.getDeliveryTag());
System.out.println(messageProperties.getReceivedRoutingKey());
System.out.println(messageProperties.getConsumerTag());
}
}
#4、 测试
先运行上述测试程序(交换机和队列才能先被声明和绑定),然后启动消费者;在消费者工程springboot-rabbitmq-consumer中控制台查看是否接收到对应消息。
另外;也可以在RabbitMQ的管理控制台中查看到交换机与队列的绑定。
#RabbitMQ高级 学习目标
-
掌握RabbitMQ 高级特性
-
理解RabbitMQ 应用问题
-
能够搭建RabbitMQ 集群
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7KFJuU1a-1682215221619)(null)]
#第一章 RabbitMQ 高级特性
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KHa0Pjaw-1682215218583)(C:\Users\陈刚\AppData\Roaming\Typora\typora-user-images\image-20230104102111891.png)]
1、消息可靠性投递
在使用 RabbitMQ 的时候,作为消息发送方希望杜绝任何消息丢失或者投递失败场景。RabbitMQ 为我们提供了两种方式用来控制消息的投递可靠性模式。
- confirm 确认模式:保证生产者的消息发送到交换机
- return **退回模式:**保证消息发送到指定的队列后,交换机会返回消息告诉给生产者该消息发到指定队列了。如果没发送到指定队列也会告诉生产者。
(1)confirm确认模式代码实现
- 创建maven工程,消息的生产者工程,项目模块名称:rabbitmq-producer-spring
- 添加依赖
3、消费端限流
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2WYC7qFC-1682215221728)(null)]
如上图所示:如果在A系统中需要维护相关的业务功能,可能需要将A系统的服务停止,那么这个时候消息的生产者还是一直会向MQ中发送待处理的消息,消费者此时服务已经关闭,导致大量的消息都会在MQ中累积。如果当A系统成功启动后,默认情况下消息的消费者会一次性将MQ中累积的大量的消息全部拉取到自己的服务,导致服务在短时间内会处理大量的业务,可能会导致系统服务的崩溃。 所以消费端限流是非常有必要的。
4、TTL
设置队列参数、交换机参数、发消息都可以用页面。
也能用代码。
TTL 全称 Time To Live(存活时间/过期时间)。当消息到达存活时间后,还没有被消费,会被自动清除。
RabbitMQ可以对消息设置过期时间,也可以对整个队列(Queue)设置过期时间。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sg53iagd-1682215221784)(null)]
ponent;
/**
-
@Created by IT李老师
-
公主号 “元动力课堂”
-
个人微 itlils
*/
@Component
public class MyListener {@RabbitListener(queues = RabbitConfig.FANOUT_QUEUE_1)
public void receiveMsg(Message message){
//业务逻辑
byte[] body = message.getBody();
System.out.println(new String(body));MessageProperties messageProperties = message.getMessageProperties(); //参数 System.out.println(messageProperties.getMessageId()); System.out.println(messageProperties.getDeliveryTag()); System.out.println(messageProperties.getReceivedRoutingKey()); System.out.println(messageProperties.getConsumerTag());
}
}
### [#](https://www.ydlclass.com/doc21xnv/distribute/rabbitmq/#_4、-测试)4、 测试
先运行上述测试程序(交换机和队列才能先被声明和绑定),然后启动消费者;在消费者工程springboot-rabbitmq-consumer中控制台查看是否接收到对应消息。
另外;也可以在RabbitMQ的管理控制台中查看到交换机与队列的绑定。
# [#](https://www.ydlclass.com/doc21xnv/distribute/rabbitmq/#rabbitmq高级-学习目标)RabbitMQ高级 学习目标
- 掌握RabbitMQ 高级特性
- 理解RabbitMQ 应用问题
- 能够搭建RabbitMQ 集群
[外链图片转存中...(img-6fFbmB6D-1682215218583)]
## [#](https://www.ydlclass.com/doc21xnv/distribute/rabbitmq/#第一章-rabbitmq-高级特性)第一章 RabbitMQ 高级特性
[外链图片转存中...(img-KHa0Pjaw-1682215218583)]
### 1、消息可靠性投递
在使用 RabbitMQ 的时候,作为消息发送方希望杜绝任何消息丢失或者投递失败场景。RabbitMQ 为我们提供了两种方式用来控制消息的投递可靠性模式。
- confirm **确认模式**:保证生产者的消息发送到交换机
- return **退回模式:**保证消息发送到指定的队列后,交换机会返回消息告诉给生产者该消息发到指定队列了。如果没发送到指定队列也会告诉生产者。
#### (1)confirm确认模式代码实现
1. 创建maven工程,消息的生产者工程,项目模块名称:rabbitmq-producer-spring
2. 添加依赖
### 3、消费端限流
[外链图片转存中...(img-FY1BgeQT-1682215218583)]
如上图所示:如果在A系统中需要维护相关的业务功能,可能需要将A系统的服务停止,那么这个时候消息的生产者还是一直会向MQ中发送待处理的消息,消费者此时服务已经关闭,导致大量的消息都会在MQ中累积。如果当A系统成功启动后,默认情况下消息的消费者会一次性将MQ中累积的大量的消息全部拉取到自己的服务,导致服务在短时间内会处理大量的业务,可能会导致系统服务的崩溃。 所以消费端限流是非常有必要的。
### 4、TTL
设置队列参数、交换机参数、发消息都可以用页面。
也能用代码。
TTL 全称 Time To Live(存活时间/过期时间)。当消息到达存活时间后,还没有被消费,会被自动清除。
RabbitMQ可以对消息设置过期时间,也可以对整个队列(Queue)设置过期时间。
[外链图片转存中...(img-xoFryDRw-1682215218584)]
可以在RabbitMQ管理控制台设置过期时间