目录
1. 集群形成的要求
2. 集群中的节点
3. CLI如何验证节点
4. RabbitMQ集群搭建
5. Nginx实现负载均衡
6. 测试
集群形成的要求
① 主机名解析
RabbitMQ节点使用短域名或全限定域名 (FQDN) 相互寻址。因此,所有集群成员的主机名必须可以从所有集群节点以及可能使用rabbitmqctl等命令行工具的机器上解析。
主机名解析可以使用任何标准操作系统提供的方法:
- DNS 记录
- 本地主机文件(例如/etc/hosts)
其中对于本地主机文件的主机名解析使用docker run --link
可在创建容器时,添加一个所连接容器的主机名到容器内 /etc/hosts
中。
② 端口访问
RabbitMQ 节点绑定到端口(打开服务器 TCP 套接字)以接受客户端和 CLI 工具连接。
集群中的节点
RabbitMQ代理操作所需的所有数据/状态都在所有节点之间复制。一个例外是消息队列,默认情况下它们驻留在一个节点上,尽管它们对所有节点都是可见和可访问的。
RabbitMQ 集群中的所有节点都是平等的:RabbitMQ核心中没有特殊节点。
CLI如何验证节点:Erlang Cookie
RabbitMQ节点和 CLI 工具(例如rabbitmqctl)使用cookie来确定是否允许它们相互通信。
为了使两个节点能够通信,它们必须具有相同的共享密钥,称为 Erlang cookie。cookie 只是一串最多 255 个字符的字母数字字符。
每个集群节点都必须有相同的cookie。
在 UNIX 系统上,cookie 通常位于 /var/lib/rabbitmq/.erlang.cookie
(由服务器使用)和$HOME/.erlang.cookie
(由 CLI 工具使用)。这里 Home
可以通过查看日志看到(docker logs CONTAINER_NAME
)。
RabbitMQ集群搭建
下面将统一使用node01的cookie,以保证每个节点有相同的cookie。
1. 创建并运行容器
# 拉取Rabbimq镜像
docker pull rabbitmq:3.9-management
# node01
docker run --name rabbitmqNode01 --hostname=node01 \
-v /home/data/rabbitmqCluster/rabbitmq01:/var/lib/rabbitmq \
-p 5673:5672 \
-p 15673:15672 \
-d rabbitmq:3.9-management
# 把rabbitmqNode01容器中的/var/lib/rabbitmq/.erlang.cookie复制到宿主机中储存
docker cp rabbitmqNode01:/var/lib/rabbitmq/.erlang.cookie /home/data/rabbitmqCluster/.erlang.cookie
# node02
docker run --name rabbitmqNode02 --hostname=node02 \
-v /home/data/rabbitmqCluster/rabbitmq02:/var/lib/rabbitmq \
-p 5674:5672 \
-p 15674:15672 \
--link rabbitmqNode01:node01 \
-d rabbitmq:3.9-management
docker cp /home/data/rabbitmqCluster/.erlang.cookie rabbitmqNode02:/var/lib/rabbitmq/.erlang.cookie
# node03
docker run --name rabbitmqNode03 --hostname=node03 \
-v /home/data/rabbitmqCluster/rabbitmq03:/var/lib/rabbitmq \
-p 5675:5672 \
-p 15675:15672 \
--link rabbitmqNode01:node01 \
--link rabbitmqNode02:node02 \
-d rabbitmq:3.9-management
docker cp /home/data/rabbitmqCluster/.erlang.cookie rabbitmqNode03:/var/lib/rabbitmq/.erlang.cookie
# 进入第一个rabbitmq节点容器
docker exec -it rabbitmqNode01 /bin/bash
# rabbitmqctl stop 会将 Erlang 虚拟机关闭,rabbitmqctl stop_app只关闭RabbitMQ 服务
rabbitmqctl stop_app
# 重置节点,将 RabbitMQ 节点返回到其原始状态。
rabbitmqctl reset
# 只启动应用服务
rabbitmqctl start_app
# 退出容器
exit
# 进入第二个rabbitmq节点容器
docker exec -it rabbitmqNode02 /bin/bash
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl join_cluster rabbit@node01
rabbitmqctl start_app
exit
# 进入第三个rabbitmq节点容器
docker exec -it rabbitmqNode03 /bin/bash
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl join_cluster rabbit@node01
rabbitmqctl start_app
exit
# 进入任一节点容器
docker exec -it rabbitmqNode01 /bin/bash
rabbitmqctl cluster_status
# 进入任一节点容器创建
# 创建账号
rabbitmqctl add_user user password
# 设置用户角色
rabbitmqctl set_user_tags user administrator
# 设置用户权限
rabbitmqctl set_permissions -p "/" user ".*" ".*" ".*"
访问以下任意一个登录查看
http://host-ip:15673
http://host-ip:15674
http://host-ip:15675
# 例如解除rabbitmqNode02
docker exec -it rabbitmqNode02 /bin/bash
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl start_app
exit
docker exec -it rabbitmqNode01 /bin/bash
rabbitmqctl forget_cluster_node rabbit@node02
Nginx实现负载均衡
# 拉取nginx镜像
docker pull nginx:1.20
# 创建nginx配置文件并使用以下配置
vim /home/data/nginx/nginx_rabbitmq_01.conf
nginx_rabbitmq_01.conf
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 10m;
client_body_buffer_size 128k;
proxy_connect_timeout 5s;
proxy_send_timeout 5s;
proxy_read_timeout 5s;
proxy_buffer_size 4k;
proxy_buffers 4 32k;
proxy_busy_buffers_size 64k;
proxy_temp_file_write_size 64k;
# rabbitmq管理界面
upstream rabbitManage {
server 120.37.100.249:15673;
server 120.37.100.249:15674;
server 120.37.100.249:15675;
}
server {
listen 15676;
server_name 120.37.100.249;
location / {
proxy_pass http://rabbitManage;
index index.html index.htm;
}
}
}
# rabbitmq通信
stream{
upstream rabbitTcp{
server 120.37.100.249:5673;
server 120.37.100.249:5674;
server 120.37.100.249:5675;
}
server {
listen 5676;
proxy_pass rabbitTcp;
}
}
根据配置文件创建自定义镜像
Dockerfile
FROM nginx:1.20
COPY nginx_rabbitmq_01.conf /etc/nginx/nginx.conf
EXPOSE 5676 15676
# 根据Dockerfile构建新的镜像
docker build -t nginx-rabbitmq:v1 .
# 使用新的镜像创建nginx容器
docker run -d --name nginxRabbitmq1 \
-p 5676:5676 \
-p 15676:15676 \
-v /home/data/nginx/nginx_rabbitmq_01.conf:/ect/nginx/nginx.conf \
nginx-rabbitmq:v1
测试
建立一个简单的SpringBoot项目进行测试。
项目结构
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
application.yml
spring:
application:
name: demo-10-cluster-simple
rabbitmq:
username: user
password: password
host: 120.37.100.249
port: 5676
# 应用服务 WEB 访问端口
server:
port: 8080
RabbitMqConfig
@Configuration
public class RabbitMqConfig {
public static final String X_EXCHANGE = "xExchange";
public static final String Y_EXCHANGE = "yExchange";
public static final String QUEUE_A = "queueA";
public static final String QUEUE_B = "queueB";
@Bean
public DirectExchange xExchange() {
return new DirectExchange(X_EXCHANGE);
}
@Bean
public DirectExchange yExchange() {
return new DirectExchange(Y_EXCHANGE);
}
@Bean
public Queue queueA() {
return QueueBuilder.durable(QUEUE_A).build();
}
@Bean
public Binding aQueueBindingX(@Qualifier("queueA") Queue queueA, @Qualifier("xExchange") DirectExchange exchange) {
return BindingBuilder.bind(queueA).to(exchange).with("routingKey");
}
@Bean
public Queue queueB() {
return QueueBuilder.durable(QUEUE_B).build();
}
@Bean
public Binding bQueueBindingX(@Qualifier("queueB") Queue queueB, @Qualifier("yExchange") DirectExchange exchange) {
return BindingBuilder.bind(queueB).to(exchange).with("routingKey");
}
}
SendMsgController
@RestController
public class SendMsgController {
@Autowired
private RabbitTemplate rabbitTemplate;
@GetMapping("sendMsg/x/{msg}")
public void sendMsg1(@PathVariable String msg) {
rabbitTemplate.convertAndSend("xExchange", "routingKey", msg);
System.out.println(msg);
}
@GetMapping("sendMsg/y/{msg}")
public void sendMsg2(@PathVariable String msg) {
rabbitTemplate.convertAndSend("yExchange", "routingKey", msg);
System.out.println(msg);
}
}
http://localhost:8080/sendMsg/x/hello
http://localhost:8080/sendMsg/y/world
15676端口