使用vmware15、centos7.9、jdk11、rabbitmq3.7.18
安装
- 因为RabbitMQ是基于erlang编写,所以在安装之前需要引入erlang运行依赖
erlang-22.0.7-1.el7.x86_64.rpm
socat-1.7.3.2-2.el7.x86_64.rpm - 引入RabbitMQ安装包
rabbitmq-server-3.7.18-1.el7.noarch.rpm
配置
- 创建配置文件
// rabbitmq-server-3.7.18根据安装版本填写
cp /usr/share/doc/rabbitmq-server-3.7.18/rabbitmq.config.example /etc/rabbitmq/rabbitmq.config
- 开启web管理
// 输入命令
rabbitmq-plugins enable rabbitmq_management
- 开启guest访问账号(56行去掉注释)
49 %% Security, Access Control
50 %% ========================
51 %%
52 %% Related doc guide: https://www.rabbitmq.com/access-control.html.
53
54 %% The default "guest" user is only permitted to access the server
55 %% via a loopback interface (e.g. localhost).
56 {loopback_users, []}
57 %%
58 %% Uncomment the following line if you want to allow access to the
59 %% guest user from anywhere on the network.
60 %% {loopback_users, []},
- 浏览器访问 http://主机IP:15672,账号密码默认为guest
队列模式
//封装的连接工具类
public class RabbitMQUtils {
private static ConnectionFactory connectionFactory;
static {
connectionFactory = new ConnectionFactory();
//设置rabbitmq主机
connectionFactory.setHost("192.168.227.202");
//设置端口号
connectionFactory.setPort(5672);
//设置连接的虚拟主机
connectionFactory.setVirtualHost("/ems");
//设置管理员
connectionFactory.setUsername("ems");
connectionFactory.setPassword("000");
}
public static Connection createConnection() {
Connection connection = null;
try {
connection = connectionFactory.newConnection();
} catch (IOException | TimeoutException e) {
e.printStackTrace();
}
return connection;
}
public static void closeConnectionAndChannel(Connection connection, Channel channel) {
try {
channel.close();
connection.close();
} catch (IOException | TimeoutException e) {
e.printStackTrace();
}
}
}
hello world
- 生产者绑定通道后,将消息直接发送到指定虚拟机的队列中
- 单个消费者直接从队列中获取消息
//生产者
public class Producer {
@Test
public void sendMessage() throws IOException, TimeoutException {
Connection connection = RabbitMQUtils.createConnection();
//通过连接获取通道
Channel channel = connection.createChannel();
//通道绑定队列
//参数:队列名称、是否持久化、是否独占队列、是否消费完成后自动删除队列、附加参数
//当队列不存在时自动创建
channel.queueDeclare("ems", true, false, false, null);
//发布消息
//参数:交换机名称、路由关键字(队列)、额外设置、消息内容
channel.basicPublish("", "ems", MessageProperties.PERSISTENT_TEXT_PLAIN, "hello".getBytes());
RabbitMQUtils.closeConnectionAndChannel(connection, channel);
}
}
/消费者
public class Consumer {
public static void main(String[] args) throws IOException {
Connection connection = RabbitMQUtils.createConnection();
Channel channel = connection.createChannel();
channel.queueDeclare("ems", true, false, false, null);
channel.basicConsume("ems", true, new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println(new String(body));
}
});
}
}
work queue
- 生产者绑定通道后,将消息发送到队列中
- 队列消息被消费时,如果开启自动确认,队列中的消息将均分给每个消费者
//生产者
public class Producer {
@Test
public void sendMessage() throws IOException {
Connection connection = RabbitMQUtils.createConnection();
Channel channel = connection.createChannel();
//参数详解queue, durable, exclusive, autoDelete, arguments
//参数1:声明队列的名称,不存在则创建
//参数2:是否设置队列持久化,服务器宕机或重启后,队列不删除
//参数3:是否设置独占队列,只允许当前connection下的通道访问
//参数4:是否设置自动删除,当队列为空,最后一个消费者断开连接时,自动删除队列
//参数5:其他配置
channel.queueDeclare("work", false, false, false, null);
for (int i = 1; i <= 20; i++) {
//参数exchange, routingKey, props, body
channel.basicPublish("", "work", null, ("message body " + i).getBytes());
}
RabbitMQUtils.closeConnectionAndChannel(connection, channel);
}
}
//消费者
public class Consumer1 {
public static void main(String[] args) throws IOException {
Connection connection = RabbitMQUtils.createConnection();
Channel channel = connection.createChannel();
channel.queueDeclare("work", false, false, false, null);
channel.basicConsume("work", true, new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println(new String(body));
}
});
}
}
- 配置“多劳多得”模式
public class Consumer1 {
public static void main(String[] args) throws IOException {
Connection connection = RabbitMQUtils.createConnection();
Channel channel = connection.createChannel();
channel.queueDeclare("work", false, false, false, null);
//设置每次只取回一个队列消息
channel.basicQos(1);
//参数2:关闭自动确认,自动确认会将队列中的消息全部取回
channel.basicConsume("work", false, new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println(new String(body));
//手动确认消息被消费完成,可处理下一条消息
channel.basicAck(envelope.getDeliveryTag(), true);
}
});
}
}
subscribe 订阅(广播)
- 生产者绑定通道,将消息发送给交换机
- 每个消费者通过临时队列从交换机获取消息
- 消费者之间获取的消息完全一致
//生产者
public class Producer {
@Test
public void sendMessage() throws IOException {
Connection connection = RabbitMQUtils.createConnection();
Channel channel = connection.createChannel();
//声明交换机,设置为fanout类型
channel.exchangeDeclare("notice", "fanout");
//向交换机发送消息
channel.basicPublish("notice", "", null, "message body".getBytes());
RabbitMQUtils.closeConnectionAndChannel(connection, channel);
}
}
//消费者
public class Consumer1 {
public static void main(String[] args) throws IOException {
Connection connection = RabbitMQUtils.createConnection();
Channel channel = connection.createChannel();
//防止先运行消费者报错,可省略
channel.exchangeDeclare("notice", "fanout");
//声明临时队列
String queue = channel.queueDeclare().getQueue();
//队列绑定交换机
channel.queueBind(queue, "notice", "");
channel.basicConsume(queue, true, new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者1 " + new String(body));
}
});
}
}
routing
- 生产者创建通道,向交换机发送指定routingKey的消息
- 消费者根据交换机中routingKey的值,选择性的获取消息
//生产者
public class Producer {
@Test
public void sendMessage() throws IOException {
Connection connection = RabbitMQUtils.createConnection();
Channel channel = connection.createChannel();
//设置交换机为direct类型
channel.exchangeDeclare("logs", "direct");
String routingKey = "warning";
channel.basicPublish("logs", routingKey, null, ("logs type is " + routingKey).getBytes());
RabbitMQUtils.closeConnectionAndChannel(connection, channel);
}
}
//消费者
public class Consumer1 {
public static void main(String[] args) throws IOException {
Connection connection = RabbitMQUtils.createConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare("logs", "direct");
String queueName = channel.queueDeclare().getQueue();
//String queue, String exchange, String routingKey, Map arguments
//设置只从交换机中获取routingKey为warning的消息
channel.queueBind(queueName, "logs", "warning", null);
//channel.queueBind(queueName, "logs", "error", null);
channel.basicConsume(queueName, true, new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println(new String(body));
}
});
}
}
topic
- topic模式是在routing模式的基础上增加了routingKey的通配符*,#
- 多个key之间使用.(点)分隔
- * 匹配单个key
- # 匹配一个或多个key
//生产者
public class Producer {
@Test
public void sendMessage() throws IOException {
Connection connection = RabbitMQUtils.createConnection();
Channel channel = connection.createChannel();
//设置交换机为topic类型
channel.exchangeDeclare("order", "topic");
String routingKey = "order.new.add";
channel.basicPublish("logs", routingKey, null, ("order message: " + routingKey).getBytes());
RabbitMQUtils.closeConnectionAndChannel(connection, channel);
}
}
//消费者
public class Consumer1 {
public static void main(String[] args) throws IOException {
Connection connection = RabbitMQUtils.createConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare("order", "topic");
String queueName = channel.queueDeclare().getQueue();
//下列两种写法都可以获取routingKey为order.new.add的消息
channel.queueBind(queueName, "order", "order.new.*", null);
//channel.queueBind(queueName, "order", "order.#", null);
channel.basicConsume(queueName, true, new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println(new String(body));
}
});
}
}
SpringBoot2整合RabbitMQ
- pom文件导入amqp场景
org.springframework.boot
spring-boot-starter-amqp
- application.yml配置RabbitMQ连接参数
spring:
application:
name: springboot_rabbitmq
rabbitmq:
host: 192.168.227.201
port: 5672
username: ems
password: "000" #密码试了试不加双引号会报错
virtual-host: /ems
- 生产者(单元测试)
@SpringBootTest(classes = SpringRabbitmqApplication.class)
@RunWith(SpringRunner.class)
public class SpringRabbitmqApplicationTests {
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void sendMessage() {
rabbitTemplate.convertAndSend("hello", "hello rabbit");
}
@Test
public void sendMessageByWork() {
for (int i = 0; i < 10; i++) {
rabbitTemplate.convertAndSend("work", "message " + i);
}
}
@Test
public void sendMessageByFanout() {
rabbitTemplate.convertAndSend("notice", "", "notice message");
}
@Test
public void sendMessageByDirect() {
rabbitTemplate.convertAndSend("logs", "error", "warning message");
}
@Test
public void sendMessageByTopic() {
rabbitTemplate.convertAndSend("order", "order.history.show", "order message");
}
}
- 消费者(以topic模式为例)
@Component
public class TopicConsumer {
@RabbitListener(bindings = @QueueBinding(
value = @Queue,
exchange = @Exchange(value = "order", type = "topic"),
key = {"order.new.*"}
))
public void consumer1(String msg) {
System.out.println("consume new order message");
}
@RabbitListener(bindings = @QueueBinding(
value = @Queue,
exchange = @Exchange(value = "order", type = "topic"),
key = {"order.#"}
))
public void consumer2(String msg) {
System.out.println("consume all order message");
}
}
- 在springboot中,交换机和队列的创建以消费者为依据,当消费者不存在时,生产者无法发布消息到队列
集群(vmware + centos7)
主备模式
- 主备集群模式特点
- 队列源数据只存放在主服务中
- 可以通过从服务器间接获取队列中的消息
- 当主服务停止,从服务器无法提供队列消息
- 向从服务发送队列消息,会存到到主服务中
- 为了防止数据丢失,请设置队列数据持久化
- 使用vmware创建三台虚拟机
- 配置主机名分别为centos7-1(主服务器)、centos7-2(从)、centos7-3(从)
vim /etc/hostname
- 配置主机IP分别为 x.x.x.201、x.x.x.202、x.x.x.203
vim /etc/sysconfig/network-scripts/ifcfg-xxx
- 每个虚拟机都修改hosts文件,主机名与IP对应
vim /etc/hosts
//格式如下
x.x.x.201 centos7-1
x.x.x.202 centos7-2
x.x.x.203 centos7-3
- 安装RabbitMQ,开启web管理及用户访问
- 开启RabbitMQ服务,浏览器打开web管理页,检查是否成功
- 关闭服务,将centos7-1中的.erlang.cookie文件同步给其他两台服务器
scp /var/lib/rabbitmq/.erlang.cookie root@centos7-2:/var/lib/rabbitmq/
scp /var/lib/rabbitmq/.erlang.cookie root@centos7-3:/var/lib/rabbitmq/
- 检查三台服务器.erlang.cookie文件内容是否一致
cat /var/lib/rabbitmq/.erlang.cookie
- 静默开启RabbitMQ服务(三台)
rabbitmq-server -detached
- 查看各自集群状态
rabbitmqctl cluster_status
- 关闭备用虚拟机RabbitMQ服务(从)
rabbitmqctl stop_app
- 从服务加入主服务集群(从)
rabbitmqctl join_cluster rabbit@centos7-1
- 开启RabbitMQ服务(从)
rabbitmqctl start_app
- 查看集群状态
rabbitmqctl cluster_status
//集群状态
Cluster status of node rabbit@centos7-1 ...
[{nodes,[{disc,['rabbit@centos7-1','rabbit@centos7-2','rabbit@centos7-3']}]},
{running_nodes,['rabbit@centos7-2','rabbit@centos7-3','rabbit@centos7-1']},
{cluster_name,<<"rabbit@centos7-1">>},
{partitions,[]},
{alarms,[{'rabbit@centos7-2',[]},
{'rabbit@centos7-3',[]},
{'rabbit@centos7-1',[]}]}]
镜像集群模式
- 个人理解为根据某种访问策略,将队列消息以镜像的形式共享给主从服务器使用
- 设置策略,在任一主备集群服务器上执行都有效
- 主服务器宕机,从服务器同样可以获取并消费消息
- 虚拟主机操作命令
rabbitmqctl add_vhost
rabbitmqctl delete_vhost
rabbitmqctl list_vhosts [ ...]
- 镜像策略操作命令
//设置策略,[]中的内容可选配
rabbitmqctl set_policy [-p ] [--priority ] [--apply-to ]
//清除
rabbitmqctl clear_policy [-p ]
//查看
rabbitmqctl list_policies [-p ]
- 设置策略参数说明
参数 | 说明 |
---|---|
-p | 可选参数,针对指定 vhost 下的exchange或 queue |
--priority | 可选参数,policy 的优先级 |
--apply-to | 可选参数,策略适用的对象类型,其值可为 “queues”, “exchanges” 或 “all”.默认是”all” |
name | policy 的名称 |
pattern | 匹配模式(正则表达式) |
definition | 镜像定义,json 格式,包括三部分(ha-mode,ha-params,ha-sync-mode) |
- definition 镜像定义参数说明
参数 | 说明 |
---|---|
ha-mode | 镜像队列模式 all:表示在集群所有节点上进行镜像; exactly:表示在指定个数的节点上镜像,节点个数由 ha-params 指定; nodes:表示在指定节点上进行镜像,节点名称通过ha-params 指定 |
ha-params | ha-mode 模式需要用到的参数 1. exactly 模式下为数字表述镜像节点数; 2. nodes 模式下为节点列表表示需要镜像的节点 |
ha-sync-mode | 队列消息的同步方式,其值可为”automatic”(自动)或”manually”(手动) |
- 示例
//为/ems虚拟主机设置名称为ha的镜像策略,匹配规则“^”为全部
rabbitmqctl set_policy -p /ems ha '^' '{"ha-mode":"all","ha-sync-mode":"automatic"}'