docker搭建rabbit集群
- RabbitMQ 集群搭建
- 1 集群方案的原理
- 2 集群搭建
- 1. 启动三个启动RabbitMQ
- 2. 进入RabbitMQ容器m1,重置rabbitmq服务
- 3. 进入RabbitMQ容器m2,加入集群:连接节点1rabbitmq服务
- 4. 进入RabbitMQ容器m3,加入集群:连接节点1rabbitmq服务
- 5.启动完成查看集群状态
- 3 集群存在问题
- 4 操作集群
- 1 配置文件
- 创建queue和exchange
- 生产者
- 消费者
- 测试
- 5 代码创建exchange和queue
RabbitMQ 集群搭建
摘要:实际生产应用中都会采用消息队列的集群方案,如果选择RabbitMQ那么有必要了解下它的集群方案原理
一般来说,如果只是为了学习RabbitMQ或者验证业务工程的正确性那么在本地环境或者测试环境上使用其单实例部署就可以了,但是出于MQ中间件本身的可靠性、并发性、吞吐量和消息堆积能力等问题的考虑,在生产环境上一般都会考虑使用RabbitMQ的集群方案。
1 集群方案的原理
RabbitMQ这款消息队列中间件产品本身是基于Erlang编写,Erlang语言天生具备分布式特性(通过同步Erlang集群各节点的magic cookie来实现)。因此,RabbitMQ天然支持Clustering。集群是保证可靠性的一种方式,同时可以通过水平扩展以达到增加消息吞吐量能力的目的,这里只需要保证erlang_cookie的参数一致集群即可通信。
2 集群搭建
主要参考官方文档
# docker run 命令解释
docker run --link 可以用来链接2个容器,使得源容器(被链接的容器)和接收容器(主动去链接的容器)之间可以互相通信。
# -p 映射一个端口
# -v 挂载数据卷
# --name为当前容器设置一个别名
# -di 启动守护式容器
# -it 启动交互式容器
# 进入容器之后执行的命令/bin/bash
# -e 设置默认参数
# --hostname 设置当前容器中虚拟机的主机名称
# --link的格式: name:hostname
# name是源容器的名称;hostname是源容器在的hostname。
1. 启动三个启动RabbitMQ
配置过程中提示使用其他配置方式
# 1.1 启动RabbitMQ1
docker run -d --hostname rabbitmq1 --name=m1 -p 15673:15672 -p 5673:5672 -e RABBITMQ_ERLANG_COOKIE='rabbitmqcookie' rabbitmq:management
# -e 注入参数,RABBITMQ_ERLANG_COOKIE: erlang_cookie参数,集群中的节点必须保持一致
# 1.2 启动RabbitMQ2
docker run -d --hostname rabbitmq2 --name=m2 -p 15674:15672 -p 5674:5672 --link m1:rabbitmq1 -e RABBITMQ_ERLANG_COOKIE='rabbitmqcookie' rabbitmq:management
# 1.3 启动RabbitMQ3
docker run -d --hostname rabbitmq3 --name m3 -p 15675:15672 -p 5675:5672 --link m2:rabbitmq2 --link m1:rabbitmq1 -e RABBITMQ_ERLANG_COOKIE='rabbitmqcookie' rabbitmq:management
2. 进入RabbitMQ容器m1,重置rabbitmq服务
- 停止rabbitmq服务
- 重置rabbitmq服务
- 启动rabbitmq服务
#进入myrabbiratmq1容器
docker exec -it m1 bash
#停止rabbit应用
rabbitmqctl stop_app
#重置rabbitmq
rabbitmqctl reset
#启动rabbit应用
rabbitmqctl start_app
3. 进入RabbitMQ容器m2,加入集群:连接节点1rabbitmq服务
- 停止rabbitmq服务
- 重置rabbitmq服务
- 加入集群:连接节点1rabbitmq服务
- 启动rabbitmq服务
#3.进入myrabbitmq2容器
docker exec -it m2 bash
#停止rabbit应用
rabbitmqctl stop_app
#重置rabbitmq
rabbitmqctl reset
#加入集群
rabbitmqctl join_cluster --ram rabbit@rabbitmq1
## --ram 设置内存节点
#启动rabbit应用
rabbitmqctl start_app
4. 进入RabbitMQ容器m3,加入集群:连接节点1rabbitmq服务
- 停止rabbitmq服务
- 重置rabbitmq服务
- 加入集群:连接节点1rabbitmq服务
- 启动rabbitmq服务
#4.进入myrabbitmq3容器
docker exec -it m3 bash
#停止rabbit应用
rabbitmqctl stop_app
#重置rabbitmq
rabbitmqctl reset
#加入集群 硬盘节点
rabbitmqctl join_cluster rabbit@rabbitmq1
#启动rabbit应用
rabbitmqctl start_app
5.启动完成查看集群状态
#查看集群状态
rabbitmqctl cluster_status
注意:
集群中必须至少存在一个磁盘节点!!!
3 集群存在问题
上述配置的RabbitMQ集群模式,并不能保证队列的高可用性,尽管交换机绑定队列的内容,可以复制到集群里的任何一个节点,但是队列内容不会复制,队列节点宕机直接导致该队列无法应用,只能等待节点重启,所以要想在队列节点宕机或故障也能正常应用,就要复制队列内容到集群里的每个节点,须要创建镜像队列。
镜像队列可以同步queue和message,当主queue挂掉,从queue中会有一个变为主queue来接替工作。
镜像队列是基于普通的集群模式的,所以你还是得先配置普通集群,然后才能设置镜像队列。
镜像队列设置后,会分一个主节点和多个从节点,如果主节点宕机,从节点会有一个选为主节点,原先的主节点起来后会变为从节点。
#设置镜像队列命令,随便在一台节点都可以执行
rabbitmqctl set_policy ha-all "^" '{"ha-mode":"all"}'
然后将主节点停止后测试是否可以正常收发消息。
docker stop m1
4 操作集群
1 配置文件
producer和consumer的配置文件
在这里插入代码片## 消息队列服务地址
#spring.rabbitmq.host=192.168.153.128
## 消息队列端口
#spring.rabbitmq.port=5672
# 消息队列账户
spring.rabbitmq.username=guest
# 消息队列账户密码
spring.rabbitmq.password=guest
# 设置虚拟主机,不设置默认是根路径(/)
spring.rabbitmq.virtual-host=/
#配置集群节点的地址
spring.rabbitmq.addresses=192.168.153.128:5673,192.168.153.128:5674,192.168.153.128:5675
创建queue和exchange
生产者
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 用于向消息队列发送消息
* @author :hodor007
* @date :Created in 2020/12/21
* @description :
* @version: 1.0
*/
@RestController
public class MessageController {
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* http://localhost:8080/direct/sendMsg?exchange=order_exchange&routingKey=order.A&msg=%E8%B4%AD%E4%B9%B0M1
* @param exchange
* @param routingKey
* @param msg
* @return
*/
@RequestMapping("/direct/sendMsg")
public String sendMessage(String exchange, String routingKey, String msg){
rabbitTemplate.convertAndSend(exchange, routingKey, msg);
return "消息已投递";
}
}
消费者
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
/**
* 消息队列order.A的接收消息的监听器
* @RabbitListener 默认自动接收,不支持手动接收
* @author :hodor007
* @date :Created in 2020/12/21
* @description :
* @version: 1.0
*/
@Component
@RabbitListener(queues = "order.A")
public class ConsumerMessageListener {
//接收消息的监听方法
@RabbitHandler
public void ReceiveMessageHandler(String msg){
System.out.println("order.A消息队列的内容" + msg);
}
}
测试
发送消息
http://localhost:8080/direct/sendMsg?exchange=order_exchange&routingKey=order.A&msg=%E8%B4%AD%E4%B9%B0M1
请求结果
5 代码创建exchange和queue
在consumer中创建
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;
/**
* @author :hodor007
* @date :Created in 2020/12/22
* @description :
* @version: 1.0
*/
@Configuration
public class RabbitConfiguration {
//创建消息队列
@Bean
public Queue orderQueue(){
/**
* 参数一:队列的名称
* 参数一:是否持久化
* 参数一:是否独占
* 参数一:是否自动删除
*/
return new Queue("test_queue",true,false,false);
}
//创建交换机
@Bean
public DirectExchange exchange(){
/**
* 参数一:队列的名称
* 参数一:是否持久化
* 参数一:是否自动删除
*/
return new DirectExchange("order_test",true, false);
}
//交换机绑定消息队列
@Bean
public Binding exchangeBingQueue(
Queue orderQueue,
DirectExchange exchange){
/*
BindingBuilder.bind(队列).to(交换机).with(路由键);
*/
return BindingBuilder.bind(orderQueue).to(exchange).with("test.queue");
}
}
启动项目自动创建队列、交换机和绑定