RabbitMQ是一种消息中间件,用于处理来自客户端的异步消息。服务端将要发送的消息放入到队列池中。接收端可以根据RabbitMQ配置的转发机制接收服务端发来的消息。
RabbitMQ依据指定的转发规则进行消息的转发、缓冲和持久化操作,主要用在多服务器间或单服务器的子系统间进行通信,是分布式系统标准的配置。
./ip-dhcp
命令自动获取IPifconfig
查看IP地址https://download.docker.com/linux/static/stable/x86_64/docker-20.10.6.tgz
这个地址可以选择自己需要的版本进行下载:
https://download.docker.com/linux/static/stable/
https://github.com/Jrohy/docker-install/
将前面下载的以下文件放入服务器的 /root/docker-install
文件夹下:
- [docker-install]
- docker-20.10.6.tgz
- install.sh
- docker.bash
执行安装:
# 进入 docker-install 文件夹
cd docker-install
# 为 docker-install 添加执行权限
chmod +x install.sh
# 安装
./install.sh -f docker-20.10.6.tgz
由于国内网络问题,需要配置加速器来加速。
修改配置文件 /etc/docker/daemon.json
下面命令直接生成文件 daemon.json
cat < /etc/docker/daemon.json
{
"registry-mirrors": [
"https://docker.mirrors.ustc.edu.cn",
"http://hub-mirror.c.163.com"
],
"max-concurrent-downloads": 10,
"log-driver": "json-file",
"log-level": "warn",
"log-opts": {
"max-size": "10m",
"max-file": "3"
},
"data-root": "/var/lib/docker"
}
EOF
重启服务
# 重新加载docker配置
sudo systemctl daemon-reload
#重启docker服务
sudo systemctl restart docker
docker info
docker run hello-world
导入镜像docker load < rabbit-image.gz
关闭防火墙
systemctl stop firewalld
systemctl disable firewalld
# 重启 docker 系统服务
systemctl restart docker
配置管理员账号密码
mkdir /etc/rabbitmq
vim /etc/rabbitmq/rabbitmq.conf
# 添加两行配置:
default_user = admin
default_pass = admin
启动Rabbitmq
docker run -d --name rabbit \
-p 5672:5672 \
-p 15672:15672 \
-v /etc/rabbitmq/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf \
-e RABBITMQ_CONFIG_FILE=/etc/rabbitmq/rabbitmq.conf \
--restart=always \
rabbitmq:management
访问测试
访问管理控制台 http://192.168.64.4:15672
用户名密码是 admin
新建空项目
添加依赖
<dependencies>
<dependency>
<groupId>com.rabbitmqgroupId>
<artifactId>amqp-clientartifactId>
<version>5.4.3version>
dependency>
dependencies>
public class Producer {
public static void main(String[] args) throws IOException, TimeoutException {
/*连接*/
ConnectionFactory f = new ConnectionFactory();
f.setHost("192.168.64.4");
f.setPort(5672);
f.setUsername("admin");
f.setPassword("admin");
Connection con = f.newConnection(); // 创建连接
Channel c = con.createChannel(); // 通信的通道
/*在服务器上创建 helloworld 队列
* 队列如果存在, 不会重复创建*/
c.queueDeclare("helloworld",false,false,false,null); //队列定义/声明
/*向队列发送消息*/
c.basicPublish("","helloworld",null,"Hello World".getBytes()); // 2. 队列名
}
}
队列名
布尔值(是否是持久队列)持久: 重启服务器队列存在 (在磁盘储存队列信息)
是否是排他队列 (独占队列) (是否可以共享队列)
队列能不能被服务器自动删除, (没有消费者时服务器是否自动删除)
队列的其他参数属性
交换机,空串是默认交换机
队列名
消息的其他参数属性 (用键值对设置参数属性, 如果没有就用 null )
public class Consumer {
public static void main(String[] args) throws IOException, TimeoutException {
/*建立连接*/
ConnectionFactory f = new ConnectionFactory();
f.setHost("192.168.64.4");
f.setPort(5672);
f.setUsername("admin");
f.setPassword("admin");
Connection con = f.newConnection(); // 创建连接
Channel c = con.createChannel(); // 通信的通道
/*创建队列*/
c.queueDeclare("helloworld",false,false,false,null); //队列定义/声明
/*创建回调对象*/
DeliverCallback deliverCallback = new DeliverCallback() {
public void handle(String s, Delivery delivery) throws IOException {
byte[] a = delivery.getBody();
String string = new String(a);
System.out.println("收到:"+ string);
}
}; // 处理消息的回调
CancelCallback cancelCallback = new CancelCallback() {
public void handle(String s) throws IOException {
}
}; // 取消消息的回调
/*从队列接收消息, 把消息传递到回调对象处理*/
// c.basicConsume("helloworld", true, 处理消息的回调对象, 取消消息的回调对象);
c.basicConsume("helloworld", true, deliverCallback, cancelCallback);
}
}
确认方式 ACK – Acknowledgment
多个消费者接收消息
public class Producer {
public static void main(String[] args) throws IOException, TimeoutException {
/*创建连接*/
ConnectionFactory f = new ConnectionFactory();
f.setHost("192.168.64.4");
f.setPort(5672);
f.setUsername("admin");
f.setPassword("admin");
Connection con = f.newConnection(); // 创建连接
Channel c = con.createChannel(); // 通信的通道
/*创建队列*/
c.queueDeclare("helloworld",false,false,false,null); //队列定义/声明
/*循环输入消息发送*/
while (true) {
System.out.println("请输入消息:");
String s = new Scanner(System.in).nextLine();
c.basicPublish("","helloworld",null,s.getBytes());
}
}
}
public class Consumer {
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory f = new ConnectionFactory();
f.setHost("192.168.64.4");
f.setPort(5672);
f.setUsername("admin");
f.setPassword("admin");
Connection con = f.newConnection(); // 创建连接
Channel c = con.createChannel(); // 通信的通道
/*创建队列*/
c.queueDeclare("helloworld",false,false,false,null); //队列定义/声明
/* 回调对象*/
DeliverCallback deliverCallback = (consumerTag, message) -> {
String s = new String(message.getBody());
System.out.println(s);
// 遍历所有字符,遇到 '.' 暂停1秒
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == '.') {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
}
System.out.println("-------------------------------消息处理完成");
};
CancelCallback cancelCallback = consumerTag -> {};
// 接收消息
c.basicConsume("helloworld",true,deliverCallback,cancelCallback);
}
}
启动多个消费者
Producer 发送消息
查看两个Consumer接收
修改成手动确认回执
produce 发送 “…” 和 1-9
Consumer2接收后进入等待
Consumer2接收后进入等待
Consumer接收到所有信息
basicPublish其他参数属性改为 持久化属性
修改Consumer 创建队列参数
修改Consumer 接收信息队列名 (task_queue)
添加队列信息测试, 打开RabbitMQ 服务器查看队列信息, 会发现有helloworld 和 task_queue 两个队列
重启 RabbitMQ 服务, 再打开RabbitMQ 服务器查看, 没有持久化的helloworld就没有了, 做了持久化的task_queue 还存在
package m3;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.Scanner;
import java.util.concurrent.TimeoutException;
/**
* 生产商
*
* @author 刘杰
* @date 2021/11/22 10:48:59
*/
public class Producer {
public static void main(String[] args) throws IOException, TimeoutException {
// 连接
ConnectionFactory f = new ConnectionFactory();
f.setHost("192.168.64.4"); // wht6.cn
f.setPort(5672);
f.setUsername("admin");
f.setPassword("admin");
Channel c = f.newConnection().createChannel();
// 创建 Fanout 类型的交换机: logs
// c.exchangeDeclare("logs", "fanout");
c.exchangeDeclare("logs", BuiltinExchangeType.FANOUT);
// 向 logs 交换机发送消息
while (true) {
System.out.print("输入消息:");
String s = new Scanner(System.in).nextLine();
// 第二个参数,对于fanout类型交换机无效
c.basicPublish("logs", "", null, s.getBytes());
}
}
}
package m3;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.UUID;
import java.util.concurrent.TimeoutException;
/**
* 消费者
*
* @author 刘杰
* @date 2021/11/22 10:08:32
*/
public class Consumer {
public static void main(String[] args) throws IOException, TimeoutException {
// 连接
ConnectionFactory f = new ConnectionFactory();
f.setHost("192.168.64.4"); // wht6.cn
f.setPort(5672);
f.setUsername("admin");
f.setPassword("admin");
Channel c = f.newConnection().createChannel();
// 1.创建随机队列 2.创建交换机 3.绑定 手动命名
String queue = UUID.randomUUID().toString();
c.queueDeclare(queue,false,true,true,null); //手动提供参数
c.exchangeDeclare("logs", BuiltinExchangeType.FANOUT);
// 第三个参数对 fanout 交换机无效
c.queueBind(queue, "logs", "");
// 正常的从随机队列接收处理消息
DeliverCallback deliverCallback = (consumerTag, message) -> {
String s = new String(message.getBody());
System.out.println("收到:"+s);
};
CancelCallback cancelCallback = consumerTag -> {};
c.basicConsume(queue,true,deliverCallback,cancelCallback);
}
}
public class Producer {
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory f = new ConnectionFactory();
f.setHost("192.168.64.4"); // wht6.cn
f.setPort(5672);
f.setUsername("admin");
f.setPassword("admin");
f.setVirtualHost("/"); //默认空间
Channel c = f.newConnection().createChannel();
c.exchangeDeclare("direct_logs", BuiltinExchangeType.DIRECT);
while (true) {
System.out.println("输入消息: ");
String s = new Scanner(System.in).nextLine();
System.out.println("输入路由键: ");
String k = new Scanner(System.in).nextLine();
/*2: 路由键关键词
* 如果用默认交换机, 路由键就是队列名*/
c.basicPublish("direct_logs", k,null, s.getBytes(StandardCharsets.UTF_8));
}
}
}
public class Consumer {
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory f = new ConnectionFactory();
f.setHost("192.168.64.4"); // wht6.cn
f.setPort(5672);
f.setUsername("admin");
f.setPassword("admin");
Channel c = f.newConnection().createChannel();
String queue = c.queueDeclare().getQueue();//服务器自动提供参数: 随机名,false,true,true .getQueue(): 得到随机名(因为后面要用)
c.exchangeDeclare("direct_logs", BuiltinExchangeType.DIRECT);
System.out.println("输入绑定键, 用空格隔开: "); // "aa bb cc"
String s = new Scanner(System.in).nextLine();
String[] a = s.split("\\s+");// \s是空白字符, +表示一到多个 (一个或多个都可以)
for (String k : a) {
c.queueBind(queue, "direct_logs", k);
}
DeliverCallback deliverCallback = (consumerTag, message) -> {
java.lang.String ss = new java.lang.String(message.getBody());
System.out.println("收到:" + ss);
};
CancelCallback cancelCallback = consumerTag -> {};
c.basicConsume(queue,true, deliverCallback, cancelCallback);
}
}
在RabbitMQ中叫做虚拟消息服务器VirtualHost,每个VirtualHost相当于一个相对独立的RabbitMQ服务器,每个VirtualHost之间是相互隔离的。exchange、queue、message不能互通
使用:
随着系统规模越来越大,微服务之间调用关系变得错综复杂,一条调用链路中可能调用多个微服务,任何一个微服务不可用都可能造整个调用过程失败
spring cloud sleuth 可以跟踪调用链路,分析链路中每个节点的执行情况
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-sleuthartifactId>
dependency>
https://github.com/openzipkin/zipkin
在根目录打开终端控制器
java -jar zipkin-server-2.23.5-exec.jar --zipkin.collector.rabbitmq.uri=amqp://admin:[email protected]:5672
如用自己的空间
java -jar zipkin-server-2.12.9-exec.jar --zipkin.collector.rabbitmq.uri=amqp://admin:[email protected]:5672 / 空间名
http://localhost:9411/zipkin
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-zipkinartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-amqpartifactId>
dependency>
添加06网关, 三个消费服务
rabbitmq:
port: 5672
host: 192.168.64.4
username: admin
password: admin
virtual-host: /
zipkin:
sender:
type: rabbit # zipkin 向 Rabbit 转发日志
课前资料/elasticsearch/pd-商城项目案例.zip 里面的 pd-web 文件夹,解压到 rabbitmq 工程目录
pom文件 SpringBoot版本改为2.3.2.RELEASE
配置连接数据库
执行启动类后配置启动类
在 working directory 中配置 pd-web 模块目录路径 然后重新启动
访问 http://localhost
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-amqpartifactId>
dependency>
rabbitmq:
host: 192.168.64.4
port: 5672
username: amdin
password: admin
virtual-host: /
@Bean
public Queue orderQueue() {
return new Queue("orderQueue", true, false, false);
}
队列参数解析
1. 持久队列
2. 独占队列
3. 不自动删除
用注解配置接收消息
收到的订单通过原来的业务方法代码, 存储到数据库