RabbitMq学习
分布式消息中间件
常用消息中间件:
ActiveMQ,RabbitMQ,Kafka,RocketMQ
应用场景:
消息中间件监控数据
异步数据传输场景
消峰填谷场景
任务调度场景
海量数据同步场景
分布式事务场景
日记管理场景
大数据分析场景
需要了解的技术:
AMQP,MQTT,持久化设计,Kafka协议,消息分发设计,高可用设计,可靠性设计,容错设计
消息协议
什么是协议
消息中间件负责数据传递,存储,和分发消费三个部分,数据存储和分发的过程中肯定要遵循某种约定成俗的规范,你是采用底层的TCP/IP,UDP还是其他自己构建的协议等,而这些约定成俗的规范就称之为协议。
网络协议的三要素
1.语法:语法是用户数据与控制信息的结构与格式,以及数据出现的顺序。
2.语义:语义是解释控制信息每个部分的意义.它规定了需要发出何种控制信息,以及完成的动作与做出什么样的响应。
3.时序:时序是对事件发生顺序的详细说明。
消息中间件采用的不是http协议,而常见的消息中间件协议有:OpenWire,AMQP,MQTT,Kafka,OpenMessage协议。
AMQP协议
AMQP(Advanced Message Queuing protocol):是高级消息队列协议,由摩根大通集团联合其他公司共同设计。是一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同的开发语言等条件限制。
特性:
1.分布事务支持。
2.消息的持久化支持
3.高性能和高可靠的消息处理优势
MQTT协议
MQTT协议:(Message Queuing Telemetry Transport)消息队列是IBM开放的一个即时通讯协议,物联网系统架构中的重要组成部分。
特点:
1.轻量
2.结构简单
3.传输快,不支持事务
4.没有持久化设计
应用场景:
1:适用于计算能力有限
2:低带宽
3:网络不稳定的场景
OpenMessage协议
最近几年由阿里,雅虎和滴滴出行,Stremalio等公司共同参与与创立的分布式消息中间件,流处理等领域的应用开发标准
特点:
1:结构简单
2:解析速度快
3:支持事务和持久性设计
Kafka协议
Kafka协议是基于TCP/IP的二进制协议。消息内部是通过长度分割,由一些基本数据类型组成。
特点是:
1.结构简单
2.解析速度快
3.无事务支持
4.有持久化设计
消息分发策略
消息的分发策略
MQ消息队列有如下几个角色
1:生产者
2:存储消息
3:消费者
那么生产者生成消息以后,MQ进行存储,消费者是如何获取消息的呢?一般获取数据的方式无外乎推(push)或者拉(pull)两种方式,典型的git就有推拉机制,我们发送的http请求就是一种典型的拉取数据库数据返回的过程。而消息队列MQ是一种推送的过程,而这些推机制会适用到很多的业务场景也有很多对应推机制策略。
消息分发策略的机制和对比
ActiveMq | RabbitMq | Kafka | RocketMq | |
---|---|---|---|---|
发布订阅 | 支持 | 支持 | 支持 | 支持 |
轮询分发 | 支持 | 支持 | 支持 | / |
公平分发 | / | 支持 | 支持 | / |
重发 | 支持 | 支持 | / | 支持 |
消息拉取 | / | 支持 | 支持 | 支持 |
消息队列高可用和高可靠
什么是高可用机制
所谓高可用:是指产品在规定的条件和规定的时刻或时间内处于可执行规定功能状态的能力,当业务量增加时,请求也过大,一台消息中间件服务器的会触及硬件的极限,一台消息服务器你已经无法满足业务需求,所以消息中间件必须支持集群部署。来达到高可用目的。
集群模式1-Master-slave主从共享数据的部署方式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9eslncFX-1672191924195)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210427104753048.png)]
生产者将消费发送到Master节点,所有的都连接这个消息队列共享这块数据区域,Master节点负责写入,一旦Master节点挂掉 ,slave节点继续服务实现高可用
集群模式2-Master-slave主从同步部署方式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aZe6WXqN-1672191924196)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210427105242471.png)]
这种模式写入消息同样在Master主节点上,但是主节点会同步数据到slave节点形成副本,和zookeeper或者redis主从机制类同,这样可以达到负载均衡的效果,如果消费者有多个这样就可以去不同的节点进行消费,因为消息的拷贝和同步会暂用很大的带宽和网络资源所以在同一个局域网中才会发挥最大的性能。
集群模式3-多主集群同步部署模式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-va6LXeYu-1672191924197)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210427105637205.png)]
和集群模式3区别不大但是它的写入可以往任意节点去写入。
集群模式4-多主集群转发部署模式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6x4cH3JR-1672191924198)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210427105800840.png)]
如果你插入的数据室在broker-1中,元数据信息会存储数据的相关描述和记录存放的位置(队列)。
它会对描述信息也就是元数据信息进行同步,如果消费者在broker-2中进行消费,发现自己几点没有对应的消息,可以从对应的元数据信息中去查询,然后返回对应的消息信息。
集群模式5-Master-slave与Breoker-cluster组合的方案
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T4lZqT6a-1672191924199)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210427110218293.png)]
实现多主多从的热备机制来完成消息的高可用以及数据的热备机制,在生产规模达到一定阶段的时候,这种使用频率比较高。这种集群模式,具体在后续的课程中进行一个分析和讲解。他们的最终目的都是为保证:消息服务器不会挂掉,出现了故障依然可以抱着消息服务继续使用。
总结:
1:要么消息共享
2:要么消息同步
3:要么元数据共享
什么是高可靠机制
所谓高可靠是指:系统可以无故障低持续运行,比如一个系统突然崩溃,报错,异常等等并不影响线上业务的正常使用运行,出错几率极低,就称之为:高可靠
在高并发业务场景中,如果不能保证系统的高可靠,那造成的隐患和损失是非常严重的。
如何保证中间消息的可靠性呢?可以从两个方面考虑:
1:消息的传输,通过协议来保证系统间数据解析的正确性。
2:消息的存储可靠:通过持久化来保证消息的可靠性。
RabbitMq安装
1.下载地址:https://www.rabbitmq.com/download.html
2.环境准备:Centos7.x 和Erlang
RabbitMq是采用Erlang语言开发的,所以系统环境必须要有Erlang环境,第一步就是安装Erlang
直接使用docker安装MQ可以省去安装Erlang的步骤
先拉取镜像带rabbitMQ管理端的
docker pull rabbitmq:management
再执行run
docker run -d --name rabbit -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=123456 -p 15672:15672 -p 5672:5672 -p 25672:25672 -p 61613:61613 -p 1883:1883 rabbitmq:management
- 访问 ip:15672进入管理界面
【 -p 15672:15672】 是控制平台docker映射到系统的对应端口
【 -p 5672:5672】 是应用程序的访问端口
RabbitMq的角色分类
1:none
不能访问management plugin
2:management:查看自己相关的节点
列出自己可以通过AMQP登入的虚拟机
查看自己的虚拟机节点virtual hosts的queues,exchanges和bindings信息
查看和关闭自己的channels和connections
查看有关自己的虚拟机节点和virtualhosts的统计信息。包括其他用户在这个节点virtualhosts中的活动信息。
**3:Policymaker **
包含management所有权限
查看和创建删除自己的virtualhosts所属的policies和paraneters信息
**4:Monitoring **
包含management所有权限
罗列出所有的virtual host,包括不能登录的virtual hosts。
查看其他用户的connections和channels信息
查看所有的virtual hosts的全局统计信息
**5:Administrator **
最高的权限
可以创建和删除virtual hosts
可以查看,创建和删除users
查看创建permisssions
什么是AMQP
什么是AMQP
AMQP全称:Advanced Message Queuing Protocol(高级消息队列协议)。 是应用层协议的一个开放标准,为面向消息的中间件设计.
AMQP生产者流转过程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pSUPh3Zp-1672191924200)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210428150602718.png)]
AMQP消费者流转过程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8tQz8SAG-1672191924202)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210428151013349.png)]
RabbitMq的核心组成部分
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m9A8XdrW-1672191924203)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210428151356636.png)]
核心概念:
Server:又称Broker,接收客户端的连接,实现AMQP实体服务。安装rabbitmq-server
Connnection:连接,应用程序与Broker的网络连接TCP/IP/三次握手和四次挥手
Channel:网络信道,几乎所有操作都在信道中进行,Channel是进行消息读写的通道,客户端可以建立对各个信道,每个信道代表一个会话任务。
Message:消息:服务与应用程序之间传送的数据,由Properties和body组成,Properties可以对消息进行装饰,比如消息优先级,延迟高特性,Body则就是消息体的内容。
Virtual Host 虚拟地址,用于进行逻辑隔离,最上层的消息路由,一个虚拟主机路由可以有若干个Exhange和Queueu,同一个虚拟主机里面不能有相同名字的Exchange
Exchange:交换机,接收消息,根据路由建发送消息到绑定的队列。(不具备消息存储能力)
Bindings:Exchange和Queue之间的虚拟连接,binding中可以保护多个routing key。
RabbitMq支持的消息模式
-
简单模式 Simple
简单的消息发送和接收
-
工作模式Work
分发机制
-
发布订阅模式
Fanout 发布订阅模式,是一种广播机制,他是没有路由key的模式
-
路由模式
有routing-key的匹配模式
-
主题Topic模式
,模糊的rounting-key的匹配模式
-
参数模式
参数匹配模式
RabbitMq -Simple 简单模式
1.实现环境
1:jdk1.8
2:构建一个maven项目
3:导入rabbitMq-server服务
4:启动rabbitMq-server服务
5:定义生产者
6:定义消费者
7:观察消费者在rabbitmq-server服务中的过程
2:导入依赖
1:java原生依赖
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.10.0</version>
</dependency>
2:spring依赖
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-amqp</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
3:springBoot依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
3.使用java代码实现简单的mq读和写操作
生产者:
package com.gf.rabbitmq.simple;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 简单模式生产者
*
*/
public class Producer {
/**
* 1.创建连接工程
* 2.创建连接Connection
* 3.通过连接获取通道Channel
* 4.通过通道创建交换机,队列,绑定关系,路由key,发送消息,和接收消息
* 5. 准备消息内容
* 6.发送消息给队列queue
* 7.关闭连接
* 8.关闭通道
*/
public static void main(String[] args) {
//1.创建连接工程
ConnectionFactory connectionFactory = new ConnectionFactory();
//设置连接ip
connectionFactory.setHost("");
//设置连接端口
connectionFactory.setPort(5672);
//设置连接账户和密码
connectionFactory.setUsername("");
connectionFactory.setPassword("");
//设置虚拟访问节点
connectionFactory.setVirtualHost("/");
Connection connection = null;
Channel channel = null;
try {
//2.创建连接Connection
connection = connectionFactory.newConnection("生产者");
//3.通过连接获取通道Channel
channel = connection.createChannel();
//4.通过通道创建交换机,队列,绑定关系,路由key,发送消息,和接收消息
String queueName = "queue1";
//第一个参数队列名称,第二个参数是否要持久化,第三参数是否具有排他性,第四个参数是否自动删除,第五个声明队列时是否要有额外的参数
channel.queueDeclare(queueName, true, false, false, null);
//5. 准备消息内容
String message = "Hello Word!1";
//6.发送消息给队列queue 参数一交换机,参数二队列或者路由key,参数三消息状态控制,参数四消息主体
channel.basicPublish("", queueName, null, message.getBytes());
System.out.println("消息发送成功!");
} catch (Exception e) {
e.printStackTrace();
} finally {
// 7.关闭通道
if (channel != null && channel.isOpen()) {
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
// 8.关闭连接
if (connection != null && connection.isOpen()) {
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
消费者
package com.gf.rabbitmq.simple;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 简单模式消费者
*
*/
public class Consumer {
/**
* 1.创建连接工程
* 2.创建连接Connection
* 3.通过连接获取通道Channel
* 4.接收消息
* 5.关闭通道
* 6.关闭连接
*/
public static void main(String[] args) {
//1.创建连接工程
ConnectionFactory connectionFactory = new ConnectionFactory();
//设置连接ip
connectionFactory.setHost("");
//设置连接端口
connectionFactory.setPort(5672);
//设置连接账户和密码
connectionFactory.setUsername("");
connectionFactory.setPassword("");
//设置虚拟访问节点
connectionFactory.setVirtualHost("/");
Connection connection = null;
Channel channel = null;
try {
//2.创建连接Connection
connection = connectionFactory.newConnection("消费者");
//3.通过连接获取通道Channel
channel = connection.createChannel();
//4.接收消息 第一个参数是读取的队列名称,第二个参数是是否自动确认消息,第三个是接收的消息内容,第四个是接收失败后的消息提示
channel.basicConsume("queue1", true, new DeliverCallback() {
public void handle(String s, Delivery delivery) throws IOException {
System.out.println("收到消息是" + new String(delivery.getBody(), "UTF-8"));
}
}, new CancelCallback() {
public void handle(String s) {
System.out.println("接收消息失败啦!");
}
});
System.out.println("开始接收消息");
} catch (Exception e) {
e.printStackTrace();
} finally {
// 5.关闭通道
if (channel != null && channel.isOpen()) {
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
// 6.关闭连接
if (connection != null && connection.isOpen()) {
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
RabbitMq -fanout 发布订阅模式
fanout是没有路由key模式的
生产者
package com.gf.rabbitmq.routing;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 发布订阅模式生产者
*
*/
public class Producer {
/**
* 1.创建连接工程
* 2.创建连接Connection
* 3.通过连接获取通道Channel
* 4.通过通道创建交换机,队列,绑定关系,路由key,发送消息,和接收消息
* 5. 准备消息内容
* 6.准备交换机
* 7.发送消息给队列queue
* 8.关闭连接
* 9.关闭通道
*/
public static void main(String[] args) {
//1.创建连接工程
ConnectionFactory connectionFactory = new ConnectionFactory();
//设置连接ip
connectionFactory.setHost("");
//设置连接端口
connectionFactory.setPort(5672);
//设置连接账户和密码
connectionFactory.setUsername("");
connectionFactory.setPassword("");
//设置虚拟访问节点
connectionFactory.setVirtualHost("/");
Connection connection = null;
Channel channel = null;
try {
//2.创建连接Connection
connection = connectionFactory.newConnection("生产者");
//3.通过连接获取通道Channel
channel = connection.createChannel();
//4.通过通道创建交换机,队列,绑定关系,路由key,发送消息,和接收消息由于是在管理端上静态绑定的所以说可以不用手动绑定队列
//5. 准备消息内容
String message = "Hello Word!1";
//6.创建一个交换机
String exchangeName = "fanout-exchange";
String type = "fanout";
//7.发送消息给队列queue 参数一交换机,参数二队列或者路由key,参数三消息状态控制,参数四消息主体
channel.basicPublish(exchangeName, "", null, message.getBytes());
System.out.println("消息发送成功!");
} catch (Exception e) {
e.printStackTrace();
} finally {
// 8.关闭通道
if (channel != null && channel.isOpen()) {
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
// 9.关闭连接
if (connection != null && connection.isOpen()) {
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
消费者
package com.gf.rabbitmq.routing;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 发布订阅模式消费者
*
*/
public class Consumer {
/**
* 1.创建连接工程
* 2.创建连接Connection
* 3.通过连接获取通道Channel
* 4.接收消息
* 5.关闭通道
* 6.关闭连接
*/
private static Runnable runnable = new Runnable() {
public void run() {
//1.创建连接工程
ConnectionFactory connectionFactory = new ConnectionFactory();
//设置连接ip
connectionFactory.setHost("");
//设置连接端口
connectionFactory.setPort(5672);
//设置连接账户和密码
connectionFactory.setUsername("");
connectionFactory.setPassword("");
//设置虚拟访问节点
connectionFactory.setVirtualHost("/");
Connection connection = null;
Channel channel = null;
final String queueName = Thread.currentThread().getName();
try {
//2.创建连接Connection
connection = connectionFactory.newConnection("消费者");
//3.通过连接获取通道Channel
channel = connection.createChannel();
//4.接收消息 第一个参数是读取的队列名称,第二个参数是是否自动确认消息,第三个是接收的消息内容,第四个是接收失败后的消息提示
channel.basicConsume(queueName, true, new DeliverCallback() {
public void handle(String s, Delivery delivery) throws IOException {
System.out.println(queueName+"收到消息是:" + new String(delivery.getBody(), "UTF-8"));
}
}, new CancelCallback() {
public void handle(String s) {
System.out.println(queueName+"接收消息失败啦!");
}
});
System.out.println(queueName+"开始接收消息");
System.in.read();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 5.关闭通道
if (channel != null && channel.isOpen()) {
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
// 6.关闭连接
if (connection != null && connection.isOpen()) {
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
};
//使用多线程的方式来模拟3个消费者来消费消息
public static void main(String[] args) {
new Thread(runnable, "test0").start();
new Thread(runnable, "test1").start();
new Thread(runnable, "test2").start();
}
}
RabbitMq - direct 路由模式
生产者
package com.gf.rabbitmq.direct;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 路由模式生产者
*
*/
public class Producer {
/**
* 1.创建连接工程
* 2.创建连接Connection
* 3.通过连接获取通道Channel
* 4.通过通道创建交换机,队列,绑定关系,路由key,发送消息,和接收消息
* 5. 准备消息内容
* 6.准备交换机
* 7.发送消息给队列queue
* 8.关闭连接
* 9.关闭通道
*/
public static void main(String[] args) {
//1.创建连接工程
ConnectionFactory connectionFactory = new ConnectionFactory();
//设置连接ip
connectionFactory.setHost("");
//设置连接端口
connectionFactory.setPort(5672);
//设置连接账户和密码
connectionFactory.setUsername("");
connectionFactory.setPassword("");
//设置虚拟访问节点
connectionFactory.setVirtualHost("/");
Connection connection = null;
Channel channel = null;
try {
//2.创建连接Connection
connection = connectionFactory.newConnection("生产者");
//3.通过连接获取通道Channel
channel = connection.createChannel();
//4.通过通道创建交换机,队列,绑定关系,路由key,发送消息,和接收消息
//5. 准备消息内容
String message = "Hello Word!12";
//6.创建一个交换机
String exchangeName = "direct_exchange";
String type = "direct";
//定义路由key来发送消息
String routingKey="sms";
//7.发送消息给队列queue 参数一交换机,参数二队列或者路由key,参数三消息状态控制,参数四消息主体
channel.basicPublish(exchangeName, routingKey, null, message.getBytes());
System.out.println("消息发送成功!");
} catch (Exception e) {
e.printStackTrace();
} finally {
// 8.关闭通道
if (channel != null && channel.isOpen()) {
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
// 9.关闭连接
if (connection != null && connection.isOpen()) {
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
消费者
package com.gf.rabbitmq.direct;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 路由模式消费者
*
*/
public class Consumer {
private static Runnable runnable = new Runnable() {
public void run() {
//1.创建连接工程
ConnectionFactory connectionFactory = new ConnectionFactory();
//设置连接ip
connectionFactory.setHost("");
//设置连接端口
connectionFactory.setPort(5672);
//设置连接账户和密码
connectionFactory.setUsername("");
connectionFactory.setPassword("");
//设置虚拟访问节点
connectionFactory.setVirtualHost("/");
Connection connection = null;
Channel channel = null;
final String queueName = Thread.currentThread().getName();
try {
//2.创建连接Connection
connection = connectionFactory.newConnection("消费者");
//3.通过连接获取通道Channel
channel = connection.createChannel();
//4.接收消息 第一个参数是读取的队列名称,第二个参数是是否自动确认消息,第三个是接收的消息内容,第四个是接收失败后的消息提示
channel.basicConsume(queueName, true, new DeliverCallback() {
public void handle(String s, Delivery delivery) throws IOException {
System.out.println(queueName+"收到消息是:" + new String(delivery.getBody(), "UTF-8"));
}
}, new CancelCallback() {
public void handle(String s) {
System.out.println(queueName+"接收消息失败啦!");
}
});
System.out.println(queueName+"开始接收消息");
System.in.read();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 5.关闭通道
if (channel != null && channel.isOpen()) {
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
// 6.关闭连接
if (connection != null && connection.isOpen()) {
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
};
/**
* 1.创建连接工程
* 2.创建连接Connection
* 3.通过连接获取通道Channel
* 4.接收消息
* 5.关闭通道
* 6.关闭连接
*/
public static void main(String[] args) {
new Thread(runnable, "queue1").start();
new Thread(runnable, "queue2").start();
new Thread(runnable, "queue3").start();
}
}
RabbitMq -Topic 模糊路由模式
生产者
package com.gf.rabbitmq.topics;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 模糊路由模式生产者
*
*/
public class Producer {
/**
* 1.创建连接工程
* 2.创建连接Connection
* 3.通过连接获取通道Channel
* 4.通过通道创建交换机,队列,绑定关系,路由key,发送消息,和接收消息
* 5. 准备消息内容
* 6.准备交换机
* 7.发送消息给队列queue
* 8.关闭连接
* 9.关闭通道
*/
public static void main(String[] args) {
//1.创建连接工程
ConnectionFactory connectionFactory = new ConnectionFactory();
//设置连接ip
connectionFactory.setHost("");
//设置连接端口
connectionFactory.setPort(5672);
//设置连接账户和密码
connectionFactory.setUsername("");
connectionFactory.setPassword("");
//设置虚拟访问节点
connectionFactory.setVirtualHost("/");
Connection connection = null;
Channel channel = null;
try {
//2.创建连接Connection
connection = connectionFactory.newConnection("生产者");
//3.通过连接获取通道Channel
channel = connection.createChannel();
//4.通过通道创建交换机,队列,绑定关系,路由key,发送消息,和接收消息
//5. 准备消息内容
String message = "Hello Word!12";
//6.创建一个交换机
String exchangeName = "topic_exchange";
String type = "topic";
//定义路由key来发送消息 如果定义的是#号代码的是可以有0级或者多级,如果是*号必须是一级并且不能为0级
//下面这个routingKey可以给以下格式的routingKey发送消息 #.com.#,*.a.#,*.a.*,#.a.#,#.order.#
String routingKey="com.a.order";
//7.发送消息给队列queue 参数一交换机,参数二队列或者路由key,参数三消息状态控制,参数四消息主体
channel.basicPublish(exchangeName, routingKey, null, message.getBytes());
System.out.println("消息发送成功!");
} catch (Exception e) {
e.printStackTrace();
} finally {
// 8.关闭通道
if (channel != null && channel.isOpen()) {
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
// 9.关闭连接
if (connection != null && connection.isOpen()) {
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
消费者
package com.gf.rabbitmq.topics;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 模糊路由模式消费者
*
*/
public class Consumer {
private static Runnable runnable = new Runnable() {
public void run() {
//1.创建连接工程
ConnectionFactory connectionFactory = new ConnectionFactory();
//设置连接ip
connectionFactory.setHost("");
//设置连接端口
connectionFactory.setPort(5672);
//设置连接账户和密码
connectionFactory.setUsername("");
connectionFactory.setPassword("");
//设置虚拟访问节点
connectionFactory.setVirtualHost("/");
Connection connection = null;
Channel channel = null;
final String queueName = Thread.currentThread().getName();
try {
//2.创建连接Connection
connection = connectionFactory.newConnection("消费者");
//3.通过连接获取通道Channel
channel = connection.createChannel();
//4.接收消息 第一个参数是读取的队列名称,第二个参数是是否自动确认消息,第三个是接收的消息内容,第四个是接收失败后的消息提示
channel.basicConsume(queueName, true, new DeliverCallback() {
public void handle(String s, Delivery delivery) throws IOException {
System.out.println(queueName+"收到消息是:" + new String(delivery.getBody(), "UTF-8"));
}
}, new CancelCallback() {
public void handle(String s) {
System.out.println(queueName+"接收消息失败啦!");
}
});
System.out.println(queueName+"开始接收消息");
System.in.read();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 5.关闭通道
if (channel != null && channel.isOpen()) {
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
// 6.关闭连接
if (connection != null && connection.isOpen()) {
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
};
/**
* 1.创建连接工程
* 2.创建连接Connection
* 3.通过连接获取通道Channel
* 4.接收消息
* 5.关闭通道
* 6.关闭连接
*/
public static void main(String[] args) {
new Thread(runnable, "queue1").start();
new Thread(runnable, "queue2").start();
new Thread(runnable, "queue3").start();
}
}
RabbitMq -work 工作模式
工作模式主要有2个子模式:
1.轮询模式的分发:一个消费者一条,按均分配
2.公平分发:根据消费者的消费能力进行公平分发,处理快的处理多,处理慢的处理少。
轮询模式
生产者
package com.gf.rabbitmq.work.lunxun;
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 gaofan@youngbai.com
* @date Created in 2021/4/28 0028 11:30
*/
public class Producer {
/**
* 1.创建连接工程
* 2.创建连接Connection
* 3.通过连接获取通道Channel
* 4.通过通道创建交换机,队列,绑定关系,路由key,发送消息,和接收消息
* 5. 准备消息内容
* 6.创建交换机
* 7.创建队列
* 8.绑定队列和交换机的关系
* 9.发送消息给队列
* 10.关闭通道
* 11.关闭连接
*/
public static void main(String[] args) {
//1.创建连接工程
ConnectionFactory connectionFactory = new ConnectionFactory();
//设置连接ip
connectionFactory.setHost("1.15.177.239");
//设置连接端口
connectionFactory.setPort(5672);
//设置连接账户和密码
connectionFactory.setUsername("admin");
connectionFactory.setPassword("123456");
//设置虚拟访问节点
connectionFactory.setVirtualHost("/");
Connection connection = null;
Channel channel = null;
try {
//2.创建连接Connection
connection = connectionFactory.newConnection("生产者");
//3.通过连接获取通道Channel
channel = connection.createChannel();
//4.通过通道创建交换机,队列,绑定关系,路由key,发送消息,和接收消息
//6.创建一个交换机
String exchangeName = "direct_test2_exchange";
//交换机类型定义direct/topic/fanout/headers
String exchangeType = "direct";
//声明一个交换机第一个参数是交换机名称,第二个参数是交换机类型,第三参数是是否持久化true是永不丢失,false重启后就会丢失
channel.exchangeDeclare(exchangeName, exchangeType, true);
//7.创建队列
//第一个参数队列名称,第二个参数是否要持久化,第三参数是否具有排他性,第四个参数是否自动删除,第五个声明队列时是否要有额外的参数
channel.queueDeclare("queue1", true, false, false, null);
//8.绑定队列和交换机的关系第一个参数是队列名称,第二个参数是交换机名称,第三个参数是路由key
channel.queueBind("queue1", exchangeName, "");
//9.发送消息给队列queue 参数一交换机,参数二队列或者路由key,参数三消息状态控制,参数四消息主体
for (int i = 0; i < 20; i++) {
//5. 准备消息内容
String message = "Hello Word!" + i;
channel.basicPublish(exchangeName, "", null, message.getBytes());
}
System.out.println("消息发送成功!");
} catch (Exception e) {
e.printStackTrace();
} finally {
// 10.关闭通道
if (channel != null && channel.isOpen()) {
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
// 11.关闭连接
if (connection != null && connection.isOpen()) {
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
消费者1
package com.gf.rabbitmq.work.lunxun;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 工作模式下的轮询模式消费者1
*
* @author gaofan@youngbai.com
* @date Created in 2021/4/28 0028 11:30
*/
public class Consumer {
/**
* 1.创建连接工程
* 2.创建连接Connection
* 3.通过连接获取通道Channel
* 4.接收消息
* 5.关闭通道
* 6.关闭连接
*/
public static void main(String[] args) {
//1.创建连接工程
ConnectionFactory connectionFactory = new ConnectionFactory();
//设置连接ip
connectionFactory.setHost("");
//设置连接端口
connectionFactory.setPort(5672);
//设置连接账户和密码
connectionFactory.setUsername("");
connectionFactory.setPassword("");
//设置虚拟访问节点
connectionFactory.setVirtualHost("/");
Connection connection = null;
Channel channel = null;
try {
//2.创建连接Connection
connection = connectionFactory.newConnection("消费者1");
//3.通过连接获取通道Channel
channel = connection.createChannel();
//4.定义接收消息的回调
//channel.basicQos(1);
//5.接收消息 第一个参数是读取的队列名称,第二个参数是是否自动确认消息,第三个是接收的消息内容,第四个是接收失败后的消息提示
channel.basicConsume("queue1", true, new DeliverCallback() {
public void handle(String s, Delivery delivery) throws IOException {
System.out.println("消费者1收到消息是:" + new String(delivery.getBody(), "UTF-8"));
}
}, new CancelCallback() {
public void handle(String s) {
System.out.println("消费者1接收消息失败啦!");
}
});
System.out.println("消费者1开始接收消息");
System.in.read();
} catch (
Exception e) {
e.printStackTrace();
} finally {
// 5.关闭通道
if (channel != null && channel.isOpen()) {
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
// 6.关闭连接
if (connection != null && connection.isOpen()) {
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
消费者2
package com.gf.rabbitmq.work.lunxun;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 工作模式下的轮询模式消费者2
*
* @author gaofan@youngbai.com
* @date Created in 2021/4/28 0028 11:30
*/
public class ConsumerTwo {
/**
* 1.创建连接工程
* 2.创建连接Connection
* 3.通过连接获取通道Channel
* 4.接收消息
* 5.关闭通道
* 6.关闭连接
*/
public static void main(String[] args) {
//1.创建连接工程
ConnectionFactory connectionFactory = new ConnectionFactory();
//设置连接ip
connectionFactory.setHost("");
//设置连接端口
connectionFactory.setPort(5672);
//设置连接账户和密码
connectionFactory.setUsername("");
connectionFactory.setPassword("");
//设置虚拟访问节点
connectionFactory.setVirtualHost("/");
Connection connection = null;
Channel channel = null;
try {
//2.创建连接Connection
connection = connectionFactory.newConnection("消费者2");
//3.通过连接获取通道Channel
channel = connection.createChannel();
//4.定义接收消息的回调
//channel.basicQos(1);
//5.接收消息 第一个参数是读取的队列名称,第二个参数是是否自动确认消息,第三个是接收的消息内容,第四个是接收失败后的消息提示
channel.basicConsume("queue1", true, new DeliverCallback() {
public void handle(String s, Delivery delivery) throws IOException {
System.out.println("消费者2收到消息是:" + new String(delivery.getBody(), "UTF-8"));
}
}, new CancelCallback() {
public void handle(String s) {
System.out.println("消费者2接收消息失败啦!");
}
});
System.out.println("消费者2开始接收消息");
System.in.read();
} catch (
Exception e) {
e.printStackTrace();
} finally {
// 5.关闭通道
if (channel != null && channel.isOpen()) {
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
// 6.关闭连接
if (connection != null && connection.isOpen()) {
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
结论
此时消费者1和消费者2同时消费20条数据结果是没个消费者得到10条数据不论服务器快慢没人分到的都是相同的数据量
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vn7pDqOf-1672191924205)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210428185541756.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iWoyOR5Z-1672191924206)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210428185553463.png)]
公平分发模式
消费者和生产者的代码几乎没变化只有消费者的代码将自动应答改为了手动应答
生产者
package com.gf.rabbitmq.work.fair;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 工作模式下的公平分发模式生产者
*
*/
public class Producer {
/**
* 1.创建连接工程
* 2.创建连接Connection
* 3.通过连接获取通道Channel
* 4.通过通道创建交换机,队列,绑定关系,路由key,发送消息,和接收消息
* 5. 准备消息内容
* 6.创建交换机
* 7.创建队列
* 8.绑定队列和交换机的关系
* 9.发送消息给队列
* 10.关闭通道
* 11.关闭连接
*/
public static void main(String[] args) {
//1.创建连接工程
ConnectionFactory connectionFactory = new ConnectionFactory();
//设置连接ip
connectionFactory.setHost("")
//设置连接端口
connectionFactory.setPort(5672);
//设置连接账户和密码
connectionFactory.setUsername("");
connectionFactory.setPassword("");
//设置虚拟访问节点
connectionFactory.setVirtualHost("/");
Connection connection = null;
Channel channel = null;
try {
//2.创建连接Connection
connection = connectionFactory.newConnection("生产者");
//3.通过连接获取通道Channel
channel = connection.createChannel();
//4.通过通道创建交换机,队列,绑定关系,路由key,发送消息,和接收消息
//6.创建一个交换机
String exchangeName = "direct_test2_exchange";
//交换机类型定义direct/topic/fanout/headers
String exchangeType = "direct";
//声明一个交换机第一个参数是交换机名称,第二个参数是交换机类型,第三参数是是否持久化true是永不丢失,false重启后就会丢失
channel.exchangeDeclare(exchangeName, exchangeType, true);
//7.创建队列
//第一个参数队列名称,第二个参数是否要持久化,第三参数是否具有排他性,第四个参数是否自动删除,第五个声明队列时是否要有额外的参数
channel.queueDeclare("queue1", true, false, false, null);
//8.绑定队列和交换机的关系第一个参数是队列名称,第二个参数是交换机名称,第三个参数是路由key
channel.queueBind("queue1", exchangeName, "");
//9.发送消息给队列queue 参数一交换机,参数二队列或者路由key,参数三消息状态控制,参数四消息主体
for (int i = 0; i < 20; i++) {
//5. 准备消息内容
String message = "Hello Word!" + i;
channel.basicPublish(exchangeName, "", null, message.getBytes());
}
System.out.println("消息发送成功!");
} catch (Exception e) {
e.printStackTrace();
} finally {
// 10.关闭通道
if (channel != null && channel.isOpen()) {
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
// 11.关闭连接
if (connection != null && connection.isOpen()) {
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
消费者1
package com.gf.rabbitmq.work.fair;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 工作模式下的轮询模式消费者1
*
* @author gaofan@youngbai.com
* @date Created in 2021/4/28 0028 11:30
*/
public class Consumer {
/**
* 1.创建连接工程
* 2.创建连接Connection
* 3.通过连接获取通道Channel
* 4.接收消息
* 5.关闭通道
* 6.关闭连接
*/
public static void main(String[] args) {
//1.创建连接工程
ConnectionFactory connectionFactory = new ConnectionFactory();
//设置连接ip
connectionFactory.setHost("1.15.177.239");
//设置连接端口
connectionFactory.setPort(5672);
//设置连接账户和密码
connectionFactory.setUsername("admin");
connectionFactory.setPassword("123456");
//设置虚拟访问节点
connectionFactory.setVirtualHost("/");
Connection connection = null;
Channel channel = null;
try {
//2.创建连接Connection
connection = connectionFactory.newConnection("消费者1");
//3.通过连接获取通道Channel
channel = connection.createChannel();
//5.接收消息 第一个参数是读取的队列名称,第二个参数是是否自动确认消息,第三个是接收的消息内容,第四个是接收失败后的消息提示公平分发必须改为手动应答
final Channel finalChannel = channel;
//4.定义接收消息的回调一次性一条
finalChannel.basicQos(1);
channel.basicConsume("queue1", false, new DeliverCallback() {
public void handle(String s, Delivery delivery) throws IOException {
try {
//加上延时模拟2台速度不一样的服务器
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("消费者1收到消息是:" + new String(delivery.getBody(), "UTF-8"));
//改为手动应答机制一次应答一条数据
finalChannel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
}, new CancelCallback() {
public void handle(String s) {
System.out.println("消费者1接收消息失败啦!");
}
});
System.out.println("消费者1开始接收消息");
System.in.read();
} catch (
Exception e) {
e.printStackTrace();
} finally {
// 5.关闭通道
if (channel != null && channel.isOpen()) {
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
// 6.关闭连接
if (connection != null && connection.isOpen()) {
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
消费者2
package com.gf.rabbitmq.work.fair;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 工作模式下的公平分发模式消费者2
*
* @author gaofan@youngbai.com
* @date Created in 2021/4/28 0028 11:30
*/
public class ConsumerTwo {
/**
* 1.创建连接工程
* 2.创建连接Connection
* 3.通过连接获取通道Channel
* 4.接收消息
* 5.关闭通道
* 6.关闭连接
*/
public static void main(String[] args) {
//1.创建连接工程
ConnectionFactory connectionFactory = new ConnectionFactory();
//设置连接ip
connectionFactory.setHost("");
//设置连接端口
connectionFactory.setPort(5672);
//设置连接账户和密码
connectionFactory.setUsername("");
connectionFactory.setPassword("");
//设置虚拟访问节点
connectionFactory.setVirtualHost("/");
Connection connection = null;
Channel channel = null;
try {
//2.创建连接Connection
connection = connectionFactory.newConnection("消费者2");
//3.通过连接获取通道Channel
channel = connection.createChannel();
//5.接收消息 第一个参数是读取的队列名称,第二个参数是是否自动确认消息,第三个是接收的消息内容,第四个是接收失败后的消息提示公平分发必须改为手动应答
final Channel finalChannel = channel;
//4.定义接收消息的回调一次性一条
finalChannel.basicQos(1);
channel.basicConsume("queue1", false, new DeliverCallback() {
public void handle(String s, Delivery delivery) throws IOException {
try {
//加上延时模拟2台速度不一样的服务器
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("消费者2收到消息是:" + new String(delivery.getBody(), "UTF-8"));
//改为手动应答机制一次应答一条数据
finalChannel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
}, new CancelCallback() {
public void handle(String s) {
System.out.println("消费者2接收消息失败啦!");
}
});
System.out.println("消费者2开始接收消息");
System.in.read();
} catch (
Exception e) {
e.printStackTrace();
} finally {
// 5.关闭通道
if (channel != null && channel.isOpen()) {
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
// 6.关闭连接
if (connection != null && connection.isOpen()) {
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
结论
由于延时的问题消费者2只消费了一条数据而消费者1消费了其他所有的数据这就是能者多劳也叫公平分发模式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZusKJtti-1672191924207)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210428190926220.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n1kxx4k7-1672191924209)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210428190935045.png)]
不通过WEB管理端创建交换机和队列,使用java代码完整的创建并绑定
生产者
package com.gf.rabbitmq.all;
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 gaofan@youngbai.com
*/
public class Producer {
/**
* 1.创建连接工程
* 2.创建连接Connection
* 3.通过连接获取通道Channel
* 4.通过通道创建交换机,队列,绑定关系,路由key,发送消息,和接收消息
* 5. 准备消息内容
* 6.创建交换机
* 7.创建队列
* 8.绑定队列和交换机的关系
* 9.发送消息给队列
* 10.关闭通道
* 11.关闭连接
*/
public static void main(String[] args) {
//1.创建连接工程
ConnectionFactory connectionFactory = new ConnectionFactory();
//设置连接ip
connectionFactory.setHost("");
//设置连接端口
connectionFactory.setPort(5672);
//设置连接账户和密码
connectionFactory.setUsername("");
connectionFactory.setPassword("");
//设置虚拟访问节点
connectionFactory.setVirtualHost("/");
Connection connection = null;
Channel channel = null;
try {
//2.创建连接Connection
connection = connectionFactory.newConnection("生产者");
//3.通过连接获取通道Channel
channel = connection.createChannel();
//4.通过通道创建交换机,队列,绑定关系,路由key,发送消息,和接收消息
//5. 准备消息内容
String message = "Hello Word!12";
//6.创建一个交换机
String exchangeName = "direct_test_exchange";
//交换机类型定义direct/topic/fanout/headers
String exchangeType = "direct";
//声明一个交换机第一个参数是交换机名称,第二个参数是交换机类型,第三参数是是否持久化true是永不丢失,false重启后就会丢失
channel.exchangeDeclare(exchangeName, exchangeType, true);
//7.创建3个队列
//第一个参数队列名称,第二个参数是否要持久化,第三参数是否具有排他性,第四个参数是否自动删除,第五个声明队列时是否要有额外的参数
for (int i = 0; i < 3; i++) {
channel.queueDeclare("test" + i, true, false, false, null);
//8.绑定队列和交换机的关系第一个参数是队列名称,第二个参数是交换机名称,第三个参数是路由key
if (i == 2) {
channel.queueBind("test" + i, exchangeName, "order");
} else {
channel.queueBind("test" + i, exchangeName, "sms");
}
}
//定义路由key来发送消息
String routingKey = "sms";
//9.发送消息给队列queue 参数一交换机,参数二队列或者路由key,参数三消息状态控制,参数四消息主体
channel.basicPublish(exchangeName, routingKey, null, message.getBytes());
System.out.println("消息发送成功!");
} catch (Exception e) {
e.printStackTrace();
} finally {
// 10.关闭通道
if (channel != null && channel.isOpen()) {
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
// 11.关闭连接
if (connection != null && connection.isOpen()) {
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
消费者
package com.gf.rabbitmq.all;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 完整的消费者
*
*/
public class Consumer {
private static Runnable runnable = new Runnable() {
public void run() {
//1.创建连接工程
ConnectionFactory connectionFactory = new ConnectionFactory();
//设置连接ip
connectionFactory.setHost("");
//设置连接端口
connectionFactory.setPort(5672);
//设置连接账户和密码
connectionFactory.setUsername("");
connectionFactory.setPassword("");
//设置虚拟访问节点
connectionFactory.setVirtualHost("/");
Connection connection = null;
Channel channel = null;
final String queueName = Thread.currentThread().getName();
try {
//2.创建连接Connection
connection = connectionFactory.newConnection("消费者");
//3.通过连接获取通道Channel
channel = connection.createChannel();
//4.接收消息 第一个参数是读取的队列名称,第二个参数是是否自动确认消息,第三个是接收的消息内容,第四个是接收失败后的消息提示
channel.basicConsume(queueName, true, new DeliverCallback() {
public void handle(String s, Delivery delivery) throws IOException {
System.out.println(queueName+"收到消息是:" + new String(delivery.getBody(), "UTF-8"));
}
}, new CancelCallback() {
public void handle(String s) {
System.out.println(queueName+"接收消息失败啦!");
}
});
System.out.println(queueName+"开始接收消息");
System.in.read();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 5.关闭通道
if (channel != null && channel.isOpen()) {
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
// 6.关闭连接
if (connection != null && connection.isOpen()) {
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
};
/**
* 1.创建连接工程
* 2.创建连接Connection
* 3.通过连接获取通道Channel
* 4.接收消息
* 5.关闭通道
* 6.关闭连接
*/
public static void main(String[] args) {
new Thread(runnable, "test0").start();
new Thread(runnable, "test1").start();
new Thread(runnable, "test2").start();
}
}
RabbitMq-springBoot-fanout模式
生产者
1.配置yml
server:
port: 8080
#配置rabbitmq服务
spring:
rabbitmq:
host:
port:
virtual-host: /
username: admin
password:
2.创建rabbitmq配置类
package com.gf.rabbitmq.springbootorderrabbitmqproducer.config;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* rabbitmq配置类
*
* @date Created in 2021/4/29 0029 16:13
*/
@Configuration
public class RabbitMqConfiguration {
/**
* 声明一个fanout类型的交换机
*
* @return * @return org.springframework.amqp.core.FanoutExchange
* @date 2021/4/29 0029 16:21
*/
@Bean
public FanoutExchange fanoutExchange() {
return new FanoutExchange("fanout_order_exchange", true, false);
}
/**
* 创建队列
*
* @return * @return org.springframework.amqp.core.FanoutExchange
* @date 2021/4/29 0029 16:21
*/
@Bean
public Queue smsQueue() {
return new Queue("sms.fanout.queue", true);
}
@Bean
public Queue emailQueue() {
return new Queue("email.fanout.queue", true);
}
/**
* 队列和交换机绑定
*
* @return * @return org.springframework.amqp.core.FanoutExchange
* @date 2021/4/29 0029 16:21
*/
@Bean
public Binding smsBinding() {
return BindingBuilder.bind(smsQueue()).to(fanoutExchange());
}
@Bean
public Binding emailBinding() {
return BindingBuilder.bind(emailQueue()).to(fanoutExchange());
}
}
3.创建server层模拟用户下订单并发送消息
package com.gf.rabbitmq.springbootorderrabbitmqproducer.service;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.UUID;
/**
* 订单服务
*
* @date Created in 2021/4/29 0029 16:07
*/
@Service
public class OrderService {
@Resource
private RabbitTemplate rabbitTemplate;
/**
* 模拟用户下单
*
* @param userId
* @param productId
* @param num
* @return * @return void
* @date 2021/4/29 0029 16:09
*/
public void makeOrder(Integer userId, Integer productId, Integer num) {
//1.根据商品id查询库存是否充足
//2.保存订单
String orderId= UUID.randomUUID().toString();
System.out.println("订单生成成功"+orderId);
//3.通过消息队列分发消息 参数一交换机,参数二:路由key/队列名称,参数三消息名称
String exchangeName="fanout_order_exchange";
rabbitTemplate.convertAndSend(exchangeName,"",orderId);
}
}
4.在测试类中测试发送消息
package com.gf.rabbitmq.springbootorderrabbitmqproducer;
import com.gf.rabbitmq.springbootorderrabbitmqproducer.service.OrderService;
import org.junit.jupiter.api.Test;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
@SpringBootTest
class SpringbootOrderRabbitmqProducerApplicationTests {
@Resource
private OrderService orderService;
@Test
void contextLoads() {
orderService.makeOrder(1, 1, 12);
}
}
消费者
1.配置yml文件
server:
port: 8081
#配置rabbitmq服务
spring:
rabbitmq:
host:
port:
virtual-host: /
username: admin
password:
2.创建接收消息的server
package com.gf.rabbitmq.springbootorderrabbitmqconsumber.service.fanout;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;
/**
* 邮件消费者
*@RabbitListener指定接收的队列
* @RabbitHandler 作为消息的落脚点
* @date Created in 2021/4/29 0029 16:31
*/
@RabbitListener(queues = {"email.fanout.queue"})
@Service
public class EmailConsumer {
@RabbitHandler
public void receiveMessage(String message) {
System.out.println("邮件接收到了订单信息:-》" + message);
}
}
package com.gf.rabbitmq.springbootorderrabbitmqconsumber.service.fanout;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;
/**
* 短信消费者
*@RabbitListener指定接收的队列
* @RabbitHandler 作为消息的落脚点
* @date Created in 2021/4/29 0029 16:32
*/
@RabbitListener(queues = {"sms.fanout.queue"})
@Service
public class SmsConsumer {
@RabbitHandler
public void receiveMessage(String message) {
System.out.println("短信接收到了订单信息:-》" + message);
}
}
RabbitMq-springBoot-Direct模式
生产者
1.配置yml
server:
port: 8080
#配置rabbitmq服务
spring:
rabbitmq:
host:
port:
virtual-host: /
username: admin
password:
2.创建rabbitmq配置类
package com.gf.rabbitmq.springbootorderrabbitmqproducer.config;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* rabbitmq配置类
*
* @date Created in 2021/4/29 0029 16:13
*/
@Configuration
public class DirectRabbitMqConfiguration {
/**
* 声明一个direct类型的交换机
*
* @return * @return org.springframework.amqp.core.directExchange
* @date 2021/4/29 0029 16:21
*/
@Bean
public DirectExchange directExchange() {
return new DirectExchange("direct_order_exchange", true, false);
}
/**
* 创建队列
*
* @return * @return org.springframework.amqp.core.directExchange
* @date 2021/4/29 0029 16:21
*/
@Bean
public Queue DirectSmsQueue() {
return new Queue("sms.direct.queue", true);
}
@Bean
public Queue DirectQueue() {
return new Queue("email.direct.queue", true);
}
/**
* 队列和交换机绑定
*
* @return * @return org.springframework.amqp.core.directExchange
* @date 2021/4/29 0029 16:21
*/
@Bean
public Binding DirectSmsBinding() {
return BindingBuilder.bind(DirectSmsQueue()).to(directExchange()).with("sms");
}
@Bean
public Binding DirectEmailBinding() {
return BindingBuilder.bind(DirectQueue()).to(directExchange()).with("email");
}
}
3.创建server层模拟用户下订单并发送消息
package com.gf.rabbitmq.springbootorderrabbitmqproducer.service;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.UUID;
/**
* 订单服务
*
* @date Created in 2021/4/29 0029 16:07
*/
@Service
public class DirectOrderService {
@Resource
private RabbitTemplate rabbitTemplate;
/**
* 模拟用户下单
*
* @param userId
* @param productId
* @param num
* @return * @return void
* @date 2021/4/29 0029 16:09
*/
public void makeOrder(Integer userId, Integer productId, Integer num) {
//1.根据商品id查询库存是否充足
//2.保存订单
String orderId= UUID.randomUUID().toString();
System.out.println("订单生成成功"+orderId);
//3.通过消息队列分发消息 参数一交换机,参数二:路由key/队列名称,参数三消息名称
String exchangeName="direct_order_exchange";
rabbitTemplate.convertAndSend(exchangeName,"sms",orderId);
}
}
4.在测试类中测试发送消息
package com.gf.rabbitmq.springbootorderrabbitmqproducer;
import com.gf.rabbitmq.springbootorderrabbitmqproducer.service.DirectOrderService;
import com.gf.rabbitmq.springbootorderrabbitmqproducer.service.OrderService;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
@SpringBootTest
class SpringbootOrderRabbitmqProducerApplicationTests {
@Resource
private DirectOrderService directOrderService;
@Test
void contextLoads() {
directOrderService.makeOrder(1, 1, 12);
}
}
消费者
1.配置yml文件
server:
port: 8081
#配置rabbitmq服务
spring:
rabbitmq:
host:
port:
virtual-host: /
username: admin
password:
2.创建接收消息的server
package com.gf.rabbitmq.springbootorderrabbitmqconsumber.service.fanout;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;
/**
* 邮件消费者
*@RabbitListener指定接收的队列
* @RabbitHandler 作为消息的落脚点
* @date Created in 2021/4/29 0029 16:31
*/
@RabbitListener(queues = {"email.direct.queue"})
@Service
public class EmailConsumer {
@RabbitHandler
public void receiveMessage(String message) {
System.out.println("邮件接收到了订单信息:-》" + message);
}
}
package com.gf.rabbitmq.springbootorderrabbitmqconsumber.service.fanout;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;
/**
* 短信消费者
*@RabbitListener指定接收的队列
* @RabbitHandler 作为消息的落脚点
* @date Created in 2021/4/29 0029 16:32
*/
@RabbitListener(queues = {"sms.direct.queue"})
@Service
public class SmsConsumer {
@RabbitHandler
public void receiveMessage(String message) {
System.out.println("短信接收到了订单信息:-》" + message);
}
}
RabbitMq-springBoot-Topic模式使用注解的方式进行绑定
生产者
1.配置yml
server:
port: 8080
#配置rabbitmq服务
spring:
rabbitmq:
host:
port:
virtual-host: /
username: admin
password:
2.不需要创建rabbitmq配置类直接在消费者服务中实现绑定
3.创建server层模拟用户下订单并发送消息
package com.gf.rabbitmq.springbootorderrabbitmqproducer.service;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.UUID;
/**
* 订单服务
*
* @author gaofan@youngbai.com
* @date Created in 2021/4/29 0029 16:07
*/
@Service
public class TopicOrderService {
@Resource
private RabbitTemplate rabbitTemplate;
/**
* 模拟用户下单
*
* @param userId
* @param productId
* @param num
* @return * @return void
* @date 2021/4/29 0029 16:09
*/
public void makeOrder(Integer userId, Integer productId, Integer num) {
//1.根据商品id查询库存是否充足
//2.保存订单
String orderId = UUID.randomUUID().toString();
System.out.println("订单生成成功" + orderId);
//3.通过消息队列分发消息 参数一交换机,参数二:路由key/队列名称,参数三消息名称
String exchangeName = "topic_order_exchange";
//*.email.#
//#.sms.#
//给短信和邮件发送消息
String routingKey = "sms.email";
rabbitTemplate.convertAndSend(exchangeName, routingKey, orderId);
}
}
4.在测试类中测试发送消息
package com.gf.rabbitmq.springbootorderrabbitmqproducer;
import com.gf.rabbitmq.springbootorderrabbitmqproducer.service.DirectOrderService;
import com.gf.rabbitmq.springbootorderrabbitmqproducer.service.OrderService;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
@SpringBootTest
class SpringbootOrderRabbitmqProducerApplicationTests {
@Resource
private TopicOrderService topicOrderService;
@Test
void contextLoads() {
topicOrderService.makeOrder(1, 1, 12);
}
}
消费者
1.配置yml文件
server:
port: 8081
#配置rabbitmq服务
spring:
rabbitmq:
host:
port:
virtual-host: /
username: admin
password:
2.创建接收消息的server
package com.gf.rabbitmq.springbootorderrabbitmqconsumber.service.topic;
import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.rabbit.annotation.*;
import org.springframework.stereotype.Service;
/**
* 邮件消费者
*
* @date Created in 2021/4/29 0029 16:31
*使用bindings进行交换机和队列的绑定 @QueueBinding中有三个参数value代表队列名称@Queue中的参数代表队列名字,是否持久化,是否自动删除,exchange代表交换机@Exchange中的参数代表交换机名称,交换机类型,key代表当前队列的路由key
*/
@RabbitListener(bindings = @QueueBinding(
value = @Queue(value = "email_topic_queue", durable = "true", autoDelete = "false"),
exchange = @Exchange(value = "topic_order_exchange", type = ExchangeTypes.TOPIC),
key = "*.email.#"
))
@Service
public class TopicEmailConsumer {
@RabbitHandler
public void receiveMessage(String message) {
System.out.println("邮件接收到了订单信息:-》" + message);
}
}
package com.gf.rabbitmq.springbootorderrabbitmqconsumber.service.topic;
import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.rabbit.annotation.*;
import org.springframework.stereotype.Service;
/**
* 短信消费者
*
* @date Created in 2021/4/29 0029 16:32
*使用bindings进行交换机和队列的绑定 @QueueBinding中有三个参数value代表队列名称@Queue中的参数代表队列名字,是否持久化,是否自动删除,exchange代表交换机@Exchange中的参数代表交换机名称,交换机类型,key代表当前队列的路由key
*/
@RabbitListener(bindings = @QueueBinding(
value = @Queue(value = "sms_topic_queue", durable = "true", autoDelete = "false"),
exchange = @Exchange(value = "topic_order_exchange", type = ExchangeTypes.TOPIC),
key = "#.sms.#"
))
@Service
public class TopicSmsConsumer {
@RabbitHandler
public void receiveMessage(String message) {
System.out.println("短信接收到了订单信息:-》" + message);
}
}
RabbitMq -过期时间TTL
概述
过期时间TTL表示可以对消息设置预期时间,这两个时间内都可以被消费者接受获取;过了之后消息自动被删除。
RabbitMq可以对消息金额队列设置TTL。目前有两种方法可以设置。
1.通过队列属性设置,队列中所有消息都是相同的过期时间
2.第二种方法是对消息进行单独设置,每条消息TTL可以不同。
如果上述两种方法同时使用,则消息的过期时间以两者之间TTL较小的那个数值为准。消息在队列的生存时间一旦超过设置的TTL值,就称为dead message被投递到死信队列,消费者无法在接收到该消息
设置队列TTL
package com.gf.rabbitmq.springbootorderrabbitmqproducer.config;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
/**
* rabbitmq配置类
*
* @date Created in 2021/4/29 0029 16:13
*/
@Configuration
public class TtlRabbitMqConfiguration {
/**
* 声明一个direct类型的交换机
*
* @return * @return org.springframework.amqp.core.directExchange
* @date 2021/4/29 0029 16:21
*/
@Bean
public DirectExchange ttlDirectExchange() {
return new DirectExchange("ttl_direct_exchange", true, false);
}
/**
* 创建队列
* 并声明队列的过期时间
*
* @return * @return org.springframework.amqp.core.directExchange
* @date 2021/4/29 0029 16:21
*/
@Bean
public Queue ttlDirectSmsQueue() {
//设置过期时间
Map<String, Object> args = new HashMap<>();
//这里一定是int类型 这个队列中的所有消息都是5秒过期
args.put("x-message-ttl", 5000);
return new Queue("ttl.sms.direct.queue", true, false, false, args);
}
/**
* 队列和交换机绑定
*
* @return * @return org.springframework.amqp.core.directExchange
* @date 2021/4/29 0029 16:21
*/
@Bean
public Binding ttlDirectSmsBinding() {
return BindingBuilder.bind(ttlDirectSmsQueue()).to(ttlDirectExchange()).with("ttl");
}
}
设置消息的TTL
/**
* 模拟用户下单
*
* @param userId
* @param productId
* @param num
* @return * @return void
* @date 2021/4/29 0029 16:09
*/
public void ttlMessage(Integer userId, Integer productId, Integer num) {
//1.根据商品id查询库存是否充足
//2.保存订单
String orderId = UUID.randomUUID().toString();
System.out.println("订单生成成功" + orderId);
//3.通过消息队列分发消息 参数一交换机,参数二:路由key/队列名称,参数三消息名称
String exchangeName = "ttl_direct_exchange";
//给消息设置过期时间
MessagePostProcessor messagePostProcessor = message -> {
//单独给消息设置5秒过期队列不需要设置过期时间
message.getMessageProperties().setExpiration("5000");
//设置UTF-8编码
message.getMessageProperties().setContentEncoding("UTF-8");
return message;
};
//将messagePostProcessor装入消息中
rabbitTemplate.convertAndSend(exchangeName, "message", orderId,messagePostProcessor);
}
RabbitMq -死信队列
概述
DLX,全称Dead-Letter-Exchange,可以称之为死信交换机M也有人称之为死信邮箱,当消息在一个队列中变成(dead message)之后,它能被重新发送到一个交换机中,这个交换机就是DLX,绑定DLX的队列就称之为死信队列。消息变成死信,可能是以下原因导致
- 消息被拒绝
- 消息过期
- 队列达到最大长度
DLX也是一个正常的交换机,和一般的交换机没有区别,它能在任何的队列上被指定,实际上就是设置某一个队列属性.当这个队列中存在死信时,RabbitMq就会自动的将这个消息重新发布到设置的DLX上去,进而被路由到另一个队列,即死信队列。
要想使用死信队列,只需要在定义队列的时候设置队列参数 x-dead-letter-exchange 指定交换机即可
设置死信队列
//先创建一个死信交换机并和死信队列绑定
package com.gf.rabbitmq.springbootorderrabbitmqproducer.config;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
/**
* rabbitmq配置类
*
* @date Created in 2021/4/29 0029 16:13
*/
@Configuration
public class DeadRabbitMqConfiguration {
/**
* 声明一个direct类型的交换机作为死信交换机
*
* @return * @return org.springframework.amqp.core.directExchange
* @date 2021/4/29 0029 16:21
*/
@Bean
public DirectExchange deadDirectExchange() {
return new DirectExchange("dead_direct_exchange", true, false);
}
/**
* 创建队列作为死信队列
*
* @return * @return org.springframework.amqp.core.directExchange
* @date 2021/4/29 0029 16:21
*/
@Bean
public Queue deadDirectSmsQueue() {
return new Queue("dead.sms.direct.queue", true);
}
/**
* 队列和交换机绑定
*
* @return * @return org.springframework.amqp.core.directExchange
* @date 2021/4/29 0029 16:21
*/
@Bean
public Binding deadDirectSmsBinding() {
return BindingBuilder.bind(deadDirectSmsQueue()).to(deadDirectExchange()).with("dead");
}
}
创建一个有过期时间的队列
package com.gf.rabbitmq.springbootorderrabbitmqproducer.config;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
/**
* rabbitmq配置类
*
* @author gaofan@youngbai.com
*/
@Configuration
public class TtlRabbitMqConfiguration {
/**
* 声明一个direct类型的交换机
*
* @return * @return org.springframework.amqp.core.directExchange
* @date 2021/4/29 0029 16:21
*/
@Bean
public DirectExchange ttlDirectExchange() {
return new DirectExchange("ttl_direct_exchange", true, false);
}
/**
* 创建队列
* 并声明队列的过期时间
*
* @return * @return org.springframework.amqp.core.directExchange
* @date 2021/4/29 0029 16:21
*/
@Bean
public Queue ttlDirectSmsQueue() {
//设置过期时间
Map<String, Object> args = new HashMap<>();
//这里一定是int类型
args.put("x-message-ttl", 5000);
//设置绑定的死信交换机
args.put("x-dead-letter-exchange","dead_direct_exchange");
//如果死信交换机是Direct模式就需要绑定他的路由key
args.put("x-dead-letter-routing-key","dead");
return new Queue("ttl.sms.direct.queue", true, false, false, args);
}
/**
* 队列和交换机绑定
*
* @return * @return org.springframework.amqp.core.directExchange
* @date 2021/4/29 0029 16:21
*/
@Bean
public Binding ttlDirectSmsBinding() {
return BindingBuilder.bind(ttlDirectSmsQueue()).to(ttlDirectExchange()).with("ttl");
}
}
RabbitMq的内存控制
当内存使用超过配置的阈值时,Rabbitmq会暂时阻塞客户端的连接,并且停止接收从客户端发来的消息,以此避免服务器崩溃,客户端与服务器的心态检测机也会失效。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aDXiYhst-1672191924210)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210430114059601.png)]
设置RabbitMQ的内存空间
#默认 使用relative相对值进行设置fraction,建议取值在0.4-0.7之间,不建议超过0.7
vm_memory_high_watermark.relative = 0.4
#使用相对值设置
vm_memory_high_watermark.absolute = 2GB
设置RabbitMq的磁盘空间
#设置磁盘空间不足50MB就报警
disk_free_limit.absolute = 50MB
#使用相对值设置
disk_free_limit.relative = 3.0
RabbitMq -集群
第一步:启动第一个节点
RABBITMQ_NODE_PORT=5672 RABBITMQ_NODENAME=rabbit-1 rabbitmq-server start &
第二步启动第二个节点rabbit-2
注意:web管理插件端口占用,所以还需要指定其web插件占用的端口号
RABBITMQ_SERVER_START_ARGS=" -rabbitmq_management listener [{port,15673}]"
sudo RABBITMQ_NODE_PORT=5673 RABBITMQ_SERVER_START_ARGS=" -rabbitmq_management listener [{port,15673}]" RABBITMQ_NODENAME=rabbit-2 rabbitmq-server start &
第三步验证是否启动成功
ps aux|grep rabbitmq
第四步操作作为主节点
#停止应用
rabbitmqctl -n rabbit-1 stop_app
#目的是清除节点上的历史数据(如果不清楚,无法将节点加入到集群)
rabbitmqctl -n rabbit-l reset
#启动应用
rabbitmqctl -n rabbit-l start_app
第五步操作为从节点
#停止应用
rabbitmqctl -n rabbit-2 stop_app
#目的是清除节点上的历史数据(如果不清楚,无法将节点加入到集群)
rabbitmqctl -n rabbit-2 reset
#将rabbit2节点加入到rabbit1(主节点)集群当中去[Server-node服务器的主机名]
rabbitmqctl -n rabbit-2 join_cluster rabbit-1@'Server-node'
#启动应用
rabbitmqctl -n rabbit-2 start_app
第六步验证集群状态
rabbitmqctl cluster_status -n rabbit-1
第七步打开图形化管理界面
rabbitmq-plugins enable rabbitmq_management
第八步设置图形化管理界面账号和密码
rabbitmqctl -n rabbit-1 add_user admin admin
rabbitmqctl -n rabbit-1 set_user_tags admin administrator
rabbitmqctl -n rabbit-1 set_permissions -p / admin ".*" ".*" ".*"
rabbitmqctl -n rabbit-2 add_user admin admin
rabbitmqctl -n rabbit-2 set_user_tags admin administrator
rabbitmqctl -n rabbit-2 set_permissions -p / admin ".*" ".*" ".*"
如果是采用多机部署方式,需要读取其中一个节点的cookie,并复制到其他节点。cookie存放在/var/lib/rabbitmq/.erlang.cookie。
列如:主机名称分别是rabbit-1,rabbit-2
1.逐个启动节点
2.配置节点的hosts文件(vim/hosts)
ip1:rabbit-1
Ip2:rabbit-2
其他步骤和单机相同
RabbitMq -分布式事务
简述
分布式事务指事务的操作位于不同的节点上,需要保证事务的AICD特效。
列如在下单场景下,库存和订单如果不在一个节点上就涉及到分布式事务了。
1.两阶段提交(2PC) 需要数据库产商的支持,java组件有atomikos等。
两阶段提交,通过引入协调者来协调参与者的行为,并最终决定这些参与者是否要真正执行事务、
存在问题
1.同步阻塞,所有事务参与者在等待其它参与者响应的时候都处于同步阻塞状态,无法进行其它操作。
2.单点问题,协调者在2pc中起到很大作用,发生故障将造成很大影响,特别是在阶段二发生故障,所有参与者会一直等待状态无法完成其它操作。
3.数据不一致,在阶段二如果协调者只发送了部分消息,此时网络故障那么只有部分参与者接收到消息,也就是说只有部分参与者提交了事务,使系统数据不一致。
4.太过保守任意一个节点失败就会导致整个事务失败,没有完善的容错机制。
2.补偿事务(TCC) 严选,阿里,蚂蚁金服
TCC就是采用的补偿机制,核心思想是:针对每一个操作,都要注册一个与其对应的确认和补偿操作。它分为三个阶段
1.Try阶段主要是对业务系统做检测及资源预留
2.Confirm阶段主要是对业务系统做确认提交,Try阶段执行成功并开始执行Confirm阶段时,默认 —Confirm阶段是不会出错的。即:只要Try成功,Confirm一定成功。
3.Cancel阶段主要是业务执行错误,需要回滚的状态下执行的业务取消,预留资源释放。
优点:和2pc比起来,实现以及流程简单一些,但数据一致性比2pc也要差一些
缺点:在第二和第三步都可能失败。TCC属于应用层的一种补偿方式,所以需要程序员在实现的时候多写一些补偿代码,在一些场景中业务流程可能用TCC不太好定义和处理。
3.本地消息表(异步确保)比如:支付宝,微信支付主动查询支付状态
本地消息表与业务数据表处于同一个数据库中,这样就可以利用本地事务来保证在对这两个表的操作满足事务特性,并且使用了消息队列来保证一致性。
4.MQ事务消息,异步场景,通用性较强,拓展高。
第一阶段Prepared消息,会拿到消息的地址.第二阶段执行本地事务,第三阶段通过第一阶段拿到的地址去访问消息,并修改状态。
也就是说在业务方法内要向消息队列提交两次请求,一次发送消息和一次确认消息。如果确认消息发送失败了RabbitMq会定期扫描消息集群中的事务消息,这时候发现了Prepared消息,它会向消息发送者确认,所以生产方需要实现一个check接口,RabbitMq会根据发送端设置的策略来决定是回滚还是继续确认发送消息。这样就保证了消息发送与本地事务同时成功或同时失败
优点:实现了最终一致性,不需要依赖本地数据库事务。
缺点:实现难道大,主流MQ不支持,RocketMq事务消息部分代码也未开源。
实战
可靠生产
第一步:先配置mq连接
spring:
profiles:
active: dev
application:
name: test-order
rabbitmq:
host:
port: 5672
virtual-host: /
username:
password:
##配置消息确认机制
publisher-confirm-type: correlated
第二步:配置mq交换机和队列
package com.gf.orderservice.config;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* mq配置
*
* @date Created in 2021/5/11 0011 11:06
*/
@Configuration
public class RabbitMqConfig {
/**
* 声明一个fanout类型的交换机
*
* @return * @return org.springframework.amqp.core.directExchange
* @date 2021/4/29 0029 16:21
*/
@Bean
public FanoutExchange fanoutExchange() {
return new FanoutExchange("order_fanout_exchange", true, false);
}
/**
* 创建队列
*
* @return * @return org.springframework.amqp.core.directExchange
* @date 2021/4/29 0029 16:21
*/
@Bean
public Queue orderFanoutQueue() {
return new Queue("order.fanout.queue", true);
}
/**
* 队列和交换机绑定
*
* @return * @return org.springframework.amqp.core.directExchange
* @date 2021/4/29 0029 16:21
*/
@Bean
public Binding fanoutOrderBinding() {
return BindingBuilder.bind(orderFanoutQueue()).to(fanoutExchange());
}
}
第三步:创建订单并向消息队列中发送消息
package com.gf.orderservice.service.impl;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gf.orderservice.dao.OrderTestMapper;
import com.gf.orderservice.model.OrderMessage;
import com.gf.orderservice.model.OrderTest;
import com.gf.orderservice.service.IOrderMessageService;
import com.gf.orderservice.service.IOrderTestService;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
/**
* <p>
* 测试订单表 服务实现类
* </p>
*
* @since 2021-05-06
*/
@Service
public class OrderTestServiceImpl extends ServiceImpl<OrderTestMapper, OrderTest> implements IOrderTestService {
@Resource
private IOrderMessageService orderMessageService;
@Resource
private RabbitTemplate rabbitTemplate;
@Override
public Boolean saveOrder(OrderTest orderTest) {
boolean save = save(orderTest);
//保存到消息冗余表中
OrderMessage message = new OrderMessage();
message.setOrderId(orderTest.getId());
orderMessageService.save(message);
//往mq中发送消息往CorrelationData中插入订单的id
rabbitTemplate.convertAndSend("order_fanout_exchange", "", JSONUtil.toJsonStr(orderTest), new CorrelationData(orderTest.getId() + ""));
return save;
}
/**
* 确认mq消息
*
* @return * @return void
* @PostConstruct 是java自己的注解, 该注解被用于修饰一个非静态的void()方法, 被该注解修饰的方法会在服务器
* 加载servlet的时候运行,并且只会被服务器执行一次,在构造函数之后执行,在init()方法之前执行
* @date 2021/5/11 0011 10:57
*/
@PostConstruct
public void regCallback() {
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
@Override
public void confirm(CorrelationData correlationData, boolean b, String s) {
System.out.println("cause:" + s);
;
//如果ack为true代表消息已经收到
Integer orderId = Integer.parseInt(correlationData.getId());
if (!b) {
//这里可能要进行其他的方式进行存储
throw new RuntimeException("MQ队列应答失败,orderId是" + orderId);
}
orderMessageService.lambdaUpdate().eq(OrderMessage::getOrderId, orderId).set(OrderMessage::getStatus, 1).update();
}
});
}
}
可靠接收
第一步:配置yml
spring:
profiles:
active: dev
application:
name: test-dispather
rabbitmq:
host:
port: 5672
virtual-host: /
username:
password:
# #开启手动ack,让程序去控制MQ消息避免接收消息时候出现异常导致mq死循环
listener:
simple:
acknowledge-mode: manual
##如果使用了try catch 一下设置都可以不用设置
retry:
enabled: true #开启重试
max-attempts: 3 #最大重试次数
initial-interval: 2000ms #重试间隔时间
第二步:配置MQ
package com.gf.dispatherserver.config;
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* rabbitmq配置类创建死信队列
*
* @date Created in 2021/4/29 0029 16:13
*/
@Configuration
public class DeadRabbitMqConfiguration {
/**
* 声明一个Fount类型的交换机
*
* @return * @return org.springframework.amqp.core.directExchange
* @date 2021/4/29 0029 16:21
*/
@Bean
public FanoutExchange deadDirectOrderExchange() {
return new FanoutExchange("dead_order_fanout_exchange", true, false);
}
/**
* 创建队列作为死信队列
*
* @return * @return org.springframework.amqp.core.directExchange
* @date 2021/4/29 0029 16:21
*/
@Bean
public Queue deadOrderDirectQueue() {
return new Queue("dead.order.fanout.queue", true);
}
/**
* 队列和交换机绑定
*
* @return * @return org.springframework.amqp.core.directExchange
* @date 2021/4/29 0029 16:21
*/
@Bean
public Binding deadFanoutOrderBinding() {
return BindingBuilder.bind(deadOrderDirectQueue()).to(deadDirectOrderExchange());
}
}
package com.gf.dispatherserver.config;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
/**
* mq配置 创建普通队列
*
* @date Created in 2021/5/11 0011 11:06
*/
@Configuration
public class RabbitMqConfig {
/**
* 声明一个direct类型的交换机
*
* @return * @return org.springframework.amqp.core.directExchange
* @date 2021/4/29 0029 16:21
*/
@Bean
public FanoutExchange fanoutExchange() {
return new FanoutExchange("order_fanout_exchange", true, false);
}
/**
* 创建队列作为死信队列
*
* @return * @return org.springframework.amqp.core.directExchange
* @date 2021/4/29 0029 16:21
*/
@Bean
public Queue orderFanoutQueue() {
//设置过期时间
Map<String, Object> args = new HashMap<>();
//设置绑定的死信交换机
args.put("x-dead-letter-exchange","dead_order_fanout_exchange");
return new Queue("order.fanout.queue", true,false,false,args);
}
/**
* 队列和交换机绑定
*
* @return * @return org.springframework.amqp.core.directExchange
* @date 2021/4/29 0029 16:21
*/
@Bean
public Binding fanoutOrderBinding() {
return BindingBuilder.bind(orderFanoutQueue()).to(fanoutExchange());
}
}
第三步:接收消息
package com.gf.dispatherserver.mq;
import cn.hutool.json.JSONUtil;
import com.gf.dispatherserver.model.DispatcherTest;
import com.gf.dispatherserver.model.OrderTest;
import com.gf.dispatherserver.service.IDispatcherTestService;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.IOException;
import java.time.LocalDateTime;
/**
* TODO
*
* @author gaofan@youngbai.com
* @date Created in 2021/5/11 0011 11:46
*/
@Service
public class DispatcherTestMq {
@Resource
private IDispatcherTestService dispatcherTestService;
private int count = 1;
@RabbitListener(queues = {"order.fanout.queue"})
public void messageConsumer(String orderMsg, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
try {
//1.获取消息队列的消息
System.out.println("收到mq的消息:" + orderMsg + ",count=" + count++);
OrderTest orderTest = JSONUtil.toBean(orderMsg, OrderTest.class);
//模拟异常
System.out.println(0 / 0);
//2.拿到消息派单处理
saveDispatcher(orderTest);
//3.手动ack应答
channel.basicAck(tag, false);
} catch (Exception e) {
//如果出现异常根据实际情况去进行重发
//重发一次后丢失还是记录到死信队列看业务需求
//参数1:tag,参数2:false 多条处理,参数3:requeue 重发
//false不会重发,会把消息丢掉或者给死信队列 true会重发
channel.basicNack(tag, false, false);
}
}
@RabbitListener(queues = {"dead.order.fanout.queue"})
public void messageConsumer2(String orderMsg, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
try {
//1.获取消息队列的消息
System.out.println("收到死信mq的消息:" + orderMsg);
OrderTest orderTest = JSONUtil.toBean(orderMsg, OrderTest.class);
//3.手动ack应答
channel.basicAck(tag, false);
} catch (Exception e) {
//如果死信队列出现异常根据实际情况去进行db的操作或者短信通知
System.out.println("出现异常进行短信通知");
System.out.println("保存到异常数据表中...");
channel.basicNack(tag, false, false);
}
}
}