RabbitMQ集群搭建
- 1、RabbitMQ集群
- 2、默认集群模式安装前准备
-
- 2.1、准备3台机器
- 2.2、启动三台机器
- [2.3、使用xshell 连接三台机器](#2.3、使用xshell 连接三台机器)
- 2.4、服务器安装erlang和RabbitMQ
- [2.5、修改三台机器的/etc/hosts 文件](#2.5、修改三台机器的/etc/hosts 文件)
- 2.6、三台机器均重启网络,使节点名生效
- 2.7、三台机器的xshell均退出,然后再重新连接
- 2.8、三台机器的防火墙处理
- [2.9、三台机器 .erlang.cookie文件保持一致](#2.9、三台机器 .erlang.cookie文件保持一致)
- 3、使用默认集群模式搭建集群
- 4、Springboot连接默认集群模式RabbitMQ集群
- 5、镜像集群介绍
1、RabbitMQ集群
RabbitMQ 的集群分两种模式,一种是默认集群模式 ,一种是镜像集群模式 ;
在RabbitMQ集群中所有的节点(一个节点就是一个RabbitMQ的broker服务器) 被归为两类:一类是磁盘节点 ,一类是内存节点 ;
磁盘节点会把集群的所有信息(比如交换机、绑定、队列等信息)持久化到磁盘中,而内存节点只会将这些信息保存到内存中,如果该节点宕机或重启,内存节点的数据会全部丢失,而磁盘节点的数据不会丢失。
1.1、默认集群模式
默认集群模式也叫 普通集群模式 或者 内置集群模式 ;
RabbitMQ默认集群模式,只会把交换机、队列、虚拟主机等元数据信息 在各个节点同步,而具体队列中的消息内容不会在各个节点中同步;
元数据:
- 队列元数据:队列名称和属性(是否可持久化,是否自动删除);
- 交换机元数据:交换器名称、类型和属性
- 绑定元数据:交换器和队列的绑定列表
- 虚拟主机元数据:虚拟主机t内的相关属性,如安全属性等;
当用户访问其中任何一个RabbitMQ节点时,查询到的queue/user/exchange/vhost等信息都是相同的;
但集群中队列的具体信息数据只在队列的拥有者节点保存,其他节点只知道队列的元数据和指向该节点的指针,所以其他节点接收到不属于该节点队列的消息时会将该消息传递给该队列的拥有者节点上;
1.1.1、为什么集群不复制队列内容和状态到所有节点?
1)存储空间;
2)性能;
如果消息需要复制到集群中每个节点,网络开销不可避免,持久化消息还需要写磁盘,占用磁盘空间。
如果有一个消息生产者或者消息消费者通过amqp-client的客户端连接到节点1进行消息的发送或接收,那么此时集群中的消息收发只与节点1相关,这个没有任何问题;
如果客户端相连的是节点2或者节点3 (队列1数据不在该节点上),那么情况又会是怎么样呢?
如果消息生产者所连接的是节点2或者节点3,此时队列1的完整数据不在该两个节点上,那么在发送消息过程中这两个节点主要起了一个路由转发作用,根据这两个节点上的元数据(也就是指向queue的owner node的指针)转发至节点1上,最终发送的消息还是会存储至节点1的队列1上;
同样,如果消息消费者所连接的节点2或者节点3,那这两个节点也会作为路由节点起到转发作用,将会从节点1的队列1中获取消息进行消费;
1.2、镜像集群模式
会把交换机、队列、虚拟主机等元数据信息 在各个节点同步,同时队列中的消息内容也会在各个节点中同步。
优点 :集群内任何一台机器发生故障,其他机器只要还正常,就可以正常使用,消息不会丢失。
缺点:由于每个机器都会存储数据,比较占用空间,对资源需求比较大。
2、默认集群模式安装前准备
2.1、准备3台机器
1、重新设置三台机器的mac地址
注意clone完,先不要启动三台机器,三台机器均要重新生成mac地址,防止clone出的机器ip地址重复。
2.2、启动三台机器
启动并查看三台机器的ip地址:
java
ip addr
2.3、使用xshell 连接三台机器
2.4、服务器安装erlang和RabbitMQ
可参考:https://blog.csdn.net/qq_46112274/article/details/142534189
2.5、修改三台机器的/etc/hosts 文件
首先需要配置一下hosts文件,因为RabbitMQ集群节点名称是读取hosts文件得到的;
java
vim /etc/hosts
java
192.168.232.132 rabbit132
192.168.232.133 rabbit133
192.168.232.134 rabbit134
2.6、三台机器均重启网络,使节点名生效
java
systemctl restart network
2.7、三台机器的xshell均退出,然后再重新连接
2.8、三台机器的防火墙处理
此处关闭防火墙是因为要做rabbitmq集群,保证集群之间的通信,如果不想关闭防火墙,也可也i通过开放指定端口的形式。
java
systemctl status firewalld
systemctl stop firewalld --关闭防火墙
systemctl disable firewalld --开机不启动防火墙
2.9、三台机器 .erlang.cookie文件保持一致
如果我们使用解压缩方式安装的RabbitMQ,那么该文件会在 用户名目录下,也就是 {用户名}目录下,也就是 用户名目录下,也就是{用户名}/.erlang.cookie;
如果我们使用rpm安装包方式进行安装,那么这个文件会在/var/lib/rabbitmq目录下;
3、使用默认集群模式搭建集群
3.1、启动三台机器
启动:
java
rabbitmq-server -detached
查看rabbitmq状态
java
rabbitmqctl status
查看rabbitmq集群状态
java
rabbitmqctl cluster_status
3.2、构建集群
三台服务器ip分别是192.168.232.132、192.168.232.133、192.168.232.134。
我们把192.168.232.133、192.168.232.134两台服务器加入192.168.232.132。
192.168.232.133加入集群
shell
# 停掉rabbitmq应用
./rabbitmqctl stop_app
# 停掉rabbitmq应用,重置rabbitmq、交换机、队列
./rabbitmqctl reset
./rabbitmqctl join_cluster rabbit@rabbit132 --ram
# 启动rabbitmq应用
./rabbitmqctl start_app
--ram 参数表示让rabbitmq132成为一个内存节点,如果不带参数默认为disk磁盘节点;
把rabbit132节点添加完之后,查看集群:以及加入成功
192.168.232.134加入集群
在rabbit134节点上也执行同样的命令,使rabbit133节点也加入到集群中。
shell
# 停掉rabbitmq应用
./rabbitmqctl stop_app
# 停掉rabbitmq应用,重置rabbitmq、交换机、队列
./rabbitmqctl reset
./rabbitmqctl join_cluster rabbit@rabbit132 --ram
# 启动rabbitmq应用
./rabbitmqctl start_app
查看集群状态
java
rabbitmqctl cluster_status
3.3、集群构建完毕
192.168.232.132为磁盘节点
192.168.232.133、192.168.232.134为内存节点
3.4、操作一个集群节点,添加用户和权限等
以下测试在192.168.232.132上进行,另外两个节点可以自动复制
java
#列出用户
rabbitmqctl list_users
# 添加用户
rabbitmqctl add_user admin 123456
#查看权限
rabbitmqctl list_permissions
#设置权限
rabbitmqctl set_permissions admin ".*" ".*" ".*"
#设置角色
rabbitmqctl set_user_tags admin administrator
3.5、分别操作每个集群节点,启动web控制台插件
java
#启动web控制台插件
./rabbitmq-plugins enable rabbitmq_management
3.6、使用浏览器访问RabbitMQ
3.7、登录WEB浏览器查看集群
使用web浏览器在其中一个节点上添加一个虚拟主机 另外两个节点都会自动复制
3.8、再次查看集群状态
当执行完操作以后我们在浏览器访问web管控台来看看效果;
随便在哪个节点打开web管控台都能看到集群环境各节点的信息;
也可以使用以下命令查看集群状态;
java
./rabbitmqctl cluster_status
3.9、一些原理
RabbitMQ底层是通过Erlang架构来实现的,所以rabbitmqctl会启动Erlang节点,并基于Erlang节点来使用Erlang系统连接RabbitMQ节点,在连接过程中需要正确的Erlang Cookie和节点名称,Erlang节点通过交换Erlang Cookie以获得认证;
4、Springboot连接默认集群模式RabbitMQ集群
4.1、application.yml配置文件(关键)
java
server:
port: 8080
spring:
application:
name: rabbit_14_cluster01
rabbitmq:
# host: 192.168.232.132 #连接单台rabbitmq服务器地址
# port: 5672 #连接单台rabbitmq服务器端口
username: 你的账号
password: 你的密码
virtual-host: power
publisher-confirm-type: correlated #开启交换机的确认模式
publisher-returns: true
listener:
simple:
acknowledge-mode: manual #开启消费者的手动确认模式
addresses: 192.168.232.132:5672,192.168.232.133:5672,192.168.232.134:5672
my:
exchangeName: exchange.cluster.01
queueName: queue.cluster.01
4.2、发送消息类
java
package com.power.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.Date;
@Service
@Slf4j
public class MessageService {
@Resource
private RabbitTemplate rabbitTemplate;
//构造方法执行后执行
@PostConstruct
public void init(){
//开启生产者的确认模式
rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
//如果交换机接收消息成功,ack返回true
if(!ack){
log.error("消息没有到达交换机,原因是:{}",cause);
//TODO 重发消息或者记录错误日志
}
});
}
@Bean
public void sendMsg(){
MessageProperties messageProperties = new MessageProperties();
//设置单条消息持久化,默认技术持久化的
messageProperties.setDeliveryMode(MessageDeliveryMode.PERSISTENT);
Message message = MessageBuilder.withBody("hello world".getBytes()).andProperties(messageProperties).build();
rabbitTemplate.convertAndSend("exchange.cluster.01","info",message);
log.info("消息发送完毕,发送时间是:"+new Date());
}
}
4.3、RabbitConfig配置类
java
package com.power.config;
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitConfig {
@Value("${my.exchangeName}")
private String exchangeName;
@Value("${my.queueName}")
private String queueName;
//创建交换机
@Bean
public DirectExchange directExchange(){
return ExchangeBuilder.directExchange(exchangeName).build();
}
//创建队列
@Bean
public Queue queue(){
return QueueBuilder.durable(queueName).build();
}
@Bean
public Binding binding(DirectExchange directExchange,Queue queue){
return BindingBuilder.bind(queue).to(directExchange).with("info");
}
}
4.4、接收消息类
java
package com.power.message;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Component
@Slf4j
public class ReceiveMsg {
@RabbitListener(queues = {"queue.cluster.01"})
public void receiveMsg(Message message, Channel channel){
//获取消息唯一标识
long deliveryTag = message.getMessageProperties().getDeliveryTag();
try{
log.info("接收到的消息为:{}",new String(message.getBody()));
// TODO 插入订单
channel.basicAck(deliveryTag,false);
}catch (Exception e){
log.error("消息处理出现问题");
try {
channel.basicNack(deliveryTag,false,true);
} catch (IOException ex) {
ex.printStackTrace();
}
throw new RuntimeException(e);
}
}
}
4.5、启动类
java
package com.power;
import com.power.service.MessageService;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import javax.annotation.Resource;
@SpringBootApplication
public class Application implements ApplicationRunner {
@Resource
private MessageService messageService;
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
@Override
public void run(ApplicationArguments args) throws Exception {
messageService.sendMsg();
}
}
4.6、pom.xml
java
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.power</groupId>
<artifactId>rabbit_14_cluster01</artifactId>
<version>1.0-SNAPSHOT</version>
<name>rabbit_14_cluster01</name>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.13</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
4.7、测试
启动服务,我们发现三台rqbbitmq集群中都有消息。
但实际上消息是在192.168.232.132上存储的,该台机器是磁盘节点,消息会持久化到磁盘,192.168.232.133和192.168.232.134是内存节点,并不会真正的存储消息。
5、镜像集群介绍
镜像模式是基于默认集群模式加上一定的配置得来的;
在默认模式下的RabbitMQ集群,它会把所有节点的交换机、绑定、队列的元数据进行复制确保所有节点都有一份相同的元数据信息。但是队列数据分为两种:一种是队列的元数据信息(比如队列的最大容量,队列的名称等配置信息),另一种是队列里面的消息;
镜像模式,则是把所有的队列数据完全同步,包括元数据信息和消息数据信息,当然这对性能肯定会有一定影响,当对数据可靠性要求较高时,可以使用镜像模式;
实现镜像模式也非常简单,它是在普通集群模式基础之上搭建而成的;
5.1、使用命令配置镜像队列
shell
./rabbitmqctl set_policy [-p Vhost] Name Pattern Definition [Priority]
-
./rabbitmqctl set_policy 固定参数
-
-p Vhost: 可选参数,针对指定vhost下的queue进行设置;
-
Name: policy的名称;(可以自己取个名字就可以)
-
Pattern: queue的匹配模式(正则表达式);
-
Definition:镜像定义,包括三个部分ha-mode, ha-params, ha-sync-mode;
shell
{"ha-mode":"exactly","ha-params":2}
ha-mode:指明镜像队列的模式,有效值为 all/exactly/nodes
all:表示在集群中所有的节点上进行镜像
exactly:表示在指定个数的节点上进行镜像,节点的个数由ha-params指定
nodes:表示在指定的节点上进行镜像,节点名称通过ha-params指定
ha-params:ha-mode模式需要用到的参数
ha-sync-mode:进行队列中消息的同步方式,有效值为automatic和manual
- priority:可选参数,policy的优先级;
5.1.1、命令示例1:
比如想配置在虚拟主机为power上,所有名字开头为policy_的队列进行镜像,镜像数量为2,那么命令如下(在任意节点执行如下命令):
java
./rabbitmqctl set_policy -p power my_policy "^policy_" '{"ha-mode":"exactly","ha-params":2,"ha-sync-mode":"automatic"}'
5.1.2、命令示例2:所有节点、所有虚拟主机、所有队列都进行镜像
所有节点所有虚拟主机所有队列进行镜像
shell
# ./rabbitmqctl set_policy [-p Vhost] Name Pattern Definition [Priority]
./rabbitmqctl set_policy ha-all "^" '{"ha-mode":"all"}'
5.1.3、命令示例3:针对某个虚拟主机所有队列进行镜像
指定的虚拟主机power上对所有队列进行镜像模式配置
java
rabbitmqctl set_policy -p power ha-all "^" '{"ha-mode":"all","ha-sync-mode":"automatic"}'
(在默认集群模式的基础上,执行上面这个命令就可以把一个默认的集群模式变成镜像集群模式)
5.2、可以登录RabbitMQ的WEB控制台配置
5.3、镜像模式优缺点
优点:集群中任何一台集群停止,都不影响其他集群接收发送消息;
缺点:集群内每台集群都会存储消息,比较耗费资源。