rabbitMQ示例代码: ribbitMQ的入门代码
假设有这样一个场景, 服务A产生数据, 而服务B,C,D需要这些数据, 那么我们可以在A服务中直接调用B,C,D服务,把数据传递到下游服务即可
但是,随着我们的应用规模不断扩大,会有更多的服务需要A的数据,如果有几十甚至几百个下游服务,而且会不断变更,再加上还要考虑下游服务出错的情况,那么A服务中调用代码的维护会极为困难
这是由于服务之间耦合度过于紧密
再来考虑用RabbitMQ解耦的情况
A服务只需要向消息服务器发送消息,而不用考虑谁需要这些数据;下游服务如果需要数据,自行从消息服务器订阅消息,不再需要数据时则取消订阅即可
如果这种瞬时高峰的情况每天只出现一次,每次只有半小时,那么我们10台服务器在多数时间都只分担每秒几十次请求,这样就有点浪费资源了
这种情况,我们就可以使用RabbitMQ来进行流量削峰,高峰情况下,瞬间出现的大量请求数据,先发送到消息队列服务器,排队等待被处理,而我们的应用,可以慢慢的从消息队列接收请求数据进行处理,这样把数据处理时间拉长,以减轻瞬时压力
这是消息队列服务器非常典型的应用场景
考虑定外卖支付成功的情况
支付后要发送支付成功的通知,再寻找外卖小哥来进行配送,而寻找外卖小哥的过程非常耗时,尤其是高峰期,可能要等待几十秒甚至更长
这样就造成整条调用链路响应非常缓慢
而如果我们引入RabbitMQ消息队列,订单数据可以发送到消息队列服务器,那么调用链路也就可以到此结束,订单系统则可以立即得到响应,整条链路的响应时间只有200毫秒左右
寻找外卖小哥的应用可以以异步的方式从消息队列接收订单消息,再执行耗时的寻找操作
在RabbitMQ中,ExchangeType常用的有direct(路由模式)、Fanout(发布订阅模式)和Topic(主题模式)三种。
# VMware
**版本**
最好使用最新的 vmware 16+
**vmware网络故障,可以重置网络**
- 编辑 --- 虚拟网络编辑器 -- 左下角按钮“还原默认设置”
- 会删除所有的虚拟网络,然后重新创建
**NAT网络**
- 统一设置成 64 网段
- 编辑 --- 虚拟网络编辑器 --- 选择 vmnet8 --- 左下角设置网段:`192.168.64.0`
**虚拟机**
- 课前资料\虚拟机\
- centos-8-2105
- centos-7-1908
- 在这两个虚拟机中提供了基础配置:
- 阿里的 yum 安装源、扩展源
- 安装了 python、pip、ansible
- ip设置的脚本工具:
- ip-dhcp --- 自动分配
- ip-static --- 设置固定ip
- 加载、启动虚拟机
1. 解压缩 centos-8
2. 双击 centos-8-2105.vmx
3. 启动
4. 按照提示选择“已复制虚拟机”
5. 用户名和密码都是 `root`
**网卡没有ip,或网卡不存在**
```shell
https://wanght.blog.csdn.net/article/details/105082294
# centos 7 禁用 NetworkManager 系统服务
systemctl stop NetworkManager
systemctl disable NetworkManager
# centos 8 开启 VMware 托管
nmcli n on
systemctl restart NetworkManager
**用 Docker 运行 Rabbitmq**
1. 从 docker-base 克隆: rabbitmq
2. 设置ip
```shell
./ip-static
ip: 192.168.64.140
ifconfig
```
3. 按照笔记用docker运行 rabbitmq
过程中需要联网下载 rabbitmq 的镜像
```shell
docker pull rabbitmq:management 在线下载镜像
执行 docker images 查看镜像
关闭防火墙
systemctl stop firewalld
systemctl disable firewalld
重启 docker 系统服务
systemctl restart docker
mkdir /etc/rabbitmq
vim /etc/rabbitmq/rabbitmq.conf
# 添加两行配置:
default_user = admin
default_pass = admin
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 \
rabbitmq:management
访问管理控制台 http://192.168.64.140:15672
用户名密码是 admin
4.0.0
cn.tedu
rabbitmq-api
1.0-SNAPSHOT
8
8
com.rabbitmq
amqp-client
5.4.3
org.apache.maven.plugins
maven-compiler-plugin
3.8.1
1.8
具体步骤:
1.链接虚拟机中的RabbitMQ
1.1获取链接工厂,connectionFactory();
1.2设置:ip地址,端口号,rabbitMQ帐号,rabbitMQ密码
1.3 connectionFactory.newConnection().createChannel()
2.创建队列
2.1管道.queuedeclare(队列名,是否是持久队列,是否是排它队列,是否是自动删除,其他参数)
3.发送消息
3.1管道.basicPublish(交换机,队列名,消息其他参数(是否持久化),发送的字节码格式)
package m1;
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 {
public static void main(String[] args) throws IOException, TimeoutException {
// 连接
ConnectionFactory f = new ConnectionFactory();
f.setHost("192.168.64.140"); // wht6.cn
f.setPort(5672);
f.setUsername("admin");
f.setPassword("admin");
Connection con = f.newConnection();
Channel c = con.createChannel();
// 创建队列 hello-world,如果队列已经存在,不会重复创建
/*
参数:
1. 队列名
2. 是否是持久队列
3. 是否是排他队列、独占队列
4. 是否自动删除(没有消费者时自动删除)
5. 其他参数属性
*/
c.queueDeclare("hello-world",false,false,false,null);
// 发送消息
/*
参数:
1. 交换机,""是一个默认的交换机
3. 消息的其他参数属性
*/
c.basicPublish(
"", "hello-world", null, "Hello world!".getBytes());
}
}
具体步骤:
1.链接虚拟机中的RabbitMQ
与上同理
2.创建队列
与上同理
3.创建回调对象
DeliverCallback对象,,CancelCallback对象
4.接收信息
basicConsume(队列名,是否需要自动确认无需回应,回调成功对象,失败对象)
package m1;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Comsumer {
public static void main(String[] args) throws IOException, TimeoutException {
// 连接
ConnectionFactory f = new ConnectionFactory();
f.setHost("192.168.64.140"); // wht6.cn
f.setPort(5672);
f.setUsername("admin");
f.setPassword("admin");
Connection con = f.newConnection();
Channel c = con.createChannel();
// 创建队列 hello-world,如果队列已经存在,不会重复创建
c.queueDeclare("hello-world",false,false,false,null);
// 创建回调对象
DeliverCallback deliverCallback = new DeliverCallback() {
public void handle(String consumerTag, Delivery message) throws IOException {
byte[] a = message.getBody();
String s = new String(a);
System.out.println("收到: "+s);
}
};
CancelCallback cancelCallback = new CancelCallback() {
public void handle(String consumerTag) throws IOException {
}
};
// 接收消息,收到的消息会传递到一个回调对象去处理
// c.basicConsume("hello-world", true, 处理消息的回调对象, 取消接收消息的回调对象);
/*
第二个参数: autoAck - auto acknowledgment 自动确认
true - 自动确认
消息一向消费者发送,服务器立即自动确认消息,删除消息
false - 手动确认
消息发出后,服务器会缓存消息,不删除,
等待消费者发回一个确认消息(回执)才删除,
如果消费者处理消息过程中崩溃或离线,
服务器会回滚消息,等待重新发送
*/
c.basicConsume("hello-world", true, deliverCallback, cancelCallback);
}
}
与简单模式的区别:
在队列第二次参数中变为true,队列持久化
在提交消息的第三个参数修改成为:
MessageProperties.PERSISTENT_BASIC 常量,消息持久化
package m2;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.MessageProperties;
import java.io.IOException;
import java.util.Scanner;
import java.util.concurrent.TimeoutException;
public class Producer {
public static void main(String[] args) throws IOException, TimeoutException {
// 连接
ConnectionFactory f = new ConnectionFactory();
f.setHost("192.168.64.140"); // wht6.cn
f.setPort(5672);
f.setUsername("admin");
f.setPassword("admin");
Connection con = f.newConnection();
Channel c = con.createChannel();
// 创建队列
// 第二个参数: 是否是持久队列
c.queueDeclare("task_queue",true,false,false,null);
//发送消息
while (true) {
System.out.print("输入消息:");
String s = new Scanner(System.in).nextLine();
c.basicPublish(
"", "task_queue", MessageProperties.PERSISTENT_BASIC, s.getBytes());
}
}
}
与简单模式的区别:
1.队列第二次参数变成了可持久化的队列
2.c.basicQos(1); 每次只收一条消息,处理完之前不收下一条
3.修改了接收消息的第二个参数,修改为false,不自动响应,
接收消息,收到的消息会传递到一个回调对象去处理4.发送回执
c.basicAck对象(回执, 是否对之前收到的所有消息一起进行确认)c.basicAck(message.getEnvelope().getDeliveryTag(), false);
package m2;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Comsumer {
public static void main(String[] args) throws IOException, TimeoutException {
// 连接
ConnectionFactory f = new ConnectionFactory();
f.setHost("192.168.64.140"); // wht6.cn
f.setPort(5672);
f.setUsername("admin");
f.setPassword("admin");
Connection con = f.newConnection();
Channel c = con.createChannel();
// 创建队列 hello-world,如果队列已经存在,不会重复创建
c.queueDeclare("task_queue",true,false,false,null);
// 创建回调对象
DeliverCallback deliverCallback = new DeliverCallback() {
public void handle(String consumerTag, Delivery message) throws IOException {
byte[] a = message.getBody();
String s = new String(a);
System.out.println("收到: "+s); // "ag.d..sfd.fs...gs..."
// 模拟耗时消息
// 遍历字符串,找 '.' 字符,每找到一个暂停一秒
for (int i = 0; i < s.length(); i++) {
if ('.' == s.charAt(i)) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
}
//发送回执
// c.basicAck(回执, 是否对之前收到的所有消息一起进行确认);
c.basicAck(message.getEnvelope().getDeliveryTag(), false);
System.out.println("---------------------------消息处理结束");
}
};
CancelCallback cancelCallback = new CancelCallback() {
public void handle(String consumerTag) throws IOException {
}
};
// 每次只收一条消息,处理完之前不收下一条
c.basicQos(1);
// 接收消息,收到的消息会传递到一个回调对象去处理
c.basicConsume("task_queue", false, deliverCallback, cancelCallback);
}
}
简单理解:生产者只需要建立连接并配置 交换机,消费者配置队列并绑定交换机即可
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;
public class Producer {
public static void main(String[] args) throws IOException, TimeoutException {
// 连接
ConnectionFactory f = new ConnectionFactory();
f.setHost("wht6.cn"); // 192.168.64.140
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);
// 向交换机发送消息
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;
public class Consumer {
public static void main(String[] args) throws IOException, TimeoutException {
// 连接
ConnectionFactory f = new ConnectionFactory();
f.setHost("wht6.cn"); // 192.168.64.140
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 = new DeliverCallback() {
public void handle(String consumerTag, Delivery message) throws IOException {
String s = new String(message.getBody());
System.out.println("收到: "+s);
}
};
CancelCallback cancelCallback = new CancelCallback() {
public void handle(String consumerTag) throws IOException {
}
};
c.basicConsume(queue, true, deliverCallback,cancelCallback);
}
}
简单理解:生产者只需要配置交换机(direct)和携带路由关键词,消费者需要配置队列,和绑定交换机及关键词
package m4;
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;
public class producer {
public static void main(String[] args) throws IOException, TimeoutException {
// 连接
ConnectionFactory f = new ConnectionFactory();
f.setHost("192.168.64.140"); // wht6.cn
f.setPort(5672);
f.setUsername("admin");
f.setPassword("admin");
Channel c = f.newConnection().createChannel();
// 创建 direct 类型交换机: direct_logs
c.exchangeDeclare("direct_logs", BuiltinExchangeType.DIRECT);
// 发送消息,消息上需要携带路由键关键词
while (true) {
System.out.print("输入消息: ");
String s = new Scanner(System.in).nextLine();
System.out.print("输入路由键: ");
String k = new Scanner(System.in).nextLine();
// 对于默认交换机,使用队列名作为路由键
c.basicPublish("direct_logs", k, null, s.getBytes());
}
}
}
package m4;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.Scanner;
import java.util.concurrent.TimeoutException;
public class Consumer {
public static void main(String[] args) throws IOException, TimeoutException {
// 连接
ConnectionFactory f = new ConnectionFactory();
f.setHost("192.168.64.140"); // wht6.cn
f.setPort(5672);
f.setUsername("admin");
f.setPassword("admin");
Channel c = f.newConnection().createChannel();
// 1.随机队列 2.交换机 3.绑定,设置绑定键
// 由服务器来自动提供队列参数: 随机命名,false,true,true
String queue = c.queueDeclare().getQueue();
c.exchangeDeclare("direct_logs", BuiltinExchangeType.DIRECT);
System.out.println("输入绑定键,用空格隔开:");// "aa bb cc dd"
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 = new DeliverCallback() {
public void handle(String consumerTag, Delivery message) throws IOException {
String s = new String(message.getBody());
// 消息携带的路由键
String k = message.getEnvelope().getRoutingKey();
System.out.println(k+" --- 收到: "+s);
}
};
CancelCallback cancelCallback = new CancelCallback() {
public void handle(String consumerTag) throws IOException {
}
};
c.basicConsume(queue, true, deliverCallback,cancelCallback);
}
}