RabbitMQ中间件部署及使用
1. RabbitMQ的部署
1.1 CentOS7.x下的直接部署
- 下载RabbitMQ安装包[rabbitmq-server-3.8.14-1.el7.noarch.rpm]:https://wwe.lanzous.com/ibMV4n0uj8f
- 下载Erlang安装包[erlang-solutions-2.0-1.noarch.rpm]:https://wwe.lanzous.com/iZRumn0uj9g
- 将安装包上传到CentOS中
- 安装
# 1. 安装erlang环境
rpm -Uvh erlang-solutions-2.0-1.noarch.rpm
yum install -y erlang
# 2. 安装socat插件
yum install -y socat
# 3. 安装rabbitMQ
rpm -Uvh rabbitmq-server-3.8.14-1.el7.noarch.rpm
yum install -y rabbitmq-server
- 启动与关闭
# 启动rabbitMQ
systemctl start rabbitmq-server
# 查看rabbitMQ状态
systemctl status rabbitmq-server
# 停止rabbitMQ
systemctl stop rabbitmq-server
# 开机启动rabbitMQ
systemctl enable rabbitmq-server
- 打开web管理
# 开启web管理插件
rabbitmq-plugins enable rabbitmq_management
# 重启rabbitMQ服务
systemctl restart rabbitmq-server
- 用户添加与设置
# 添加用户(admin admin为用户账号和密码)
rabbitmqctl add_user admin admin
# 设置用户为管理员
rabbitmqctl set_user_tags admin administrator
# 为用户添加资源权限
rabbitmqctl set_permissions -p / admin ".*" ".*" ".*"
1.2 CentOS7.x下通过Docker的安装部署
- 安装Docker
# 1. 将yum包更新到最新
yum update
# 2. 安装依赖
yum install -y yum-utils device-mapper-persistent-data lvm2
# 3. 设置yum源为阿里云
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# 4. 安装docker
yum install -y docker-ce
# 5. 安装加速镜像
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors":["https://hub-mirror.c.163.com/"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
- Docker的启动和关闭
# 启动docker
systemctl start docker
# 关闭docker
systemctl stop docker
- 通过Docker安装rabbitMQ(包含用户与web管理)
docker run -di --name rabbitmq -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin -p 5672:5672 -p 15672:15672 -p 25672:25672 -p 61613:61613 -p 1883:1883 rabbitmq:management
1.3 RabbitMQ的web管理访问
- 阿里云、腾讯云等的服务器需通过安全组打开15672、5672等端口
- 其他服务器需通过防火墙打开15672、5672等端口
# 开放15672和5672端口
firewall-cmd --zone=public --add-port=15672/tcp --permanent
firewall-cmd --zone=public --add-port=5672/tcp --permanent
# 配置立即生效
firewall-cmd --reload
# 其他相关命令
# 关闭15672和5672端口
firewall-cmd --zone=public --remove-port=15672/tcp --permanent
firewall-cmd --zone=public --remove-port=5672/tcp --permanent
# 查看防火墙所有开放的端口
firewall-cmd --zone=public --list-ports
- 输入http://ip:15672/访问web管理
2. RabbitMQ的使用
2.1 SpringBoot整合RabbitMQ
- 新建Spring项目
- pom.xml依赖
<dependencies>
<!--RabbitMQ启动依赖-->
<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>
<!--测试依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
- application.yml配置
# 服务器端口
server:
port: 8080
# RabbitMQ服务配置
spring:
rabbitmq:
username: admin
password: admin
virtual-host: /
host: 192.168.150.128
port: 5672
- RabbitMQConfiguration消息队列配置类
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;
/**
* RabbitMQ配置类
*/
@Configuration
public class RabbitMQConfiguration {
/**
* 声明交换机(类型有DirectExchange、FanoutExchange、TopicExchange、HeadersExchange)
*/
@Bean
public DirectExchange directExchange() {
/*
@param1 交换机的名称
@param2 交换机是否持久化
@param3 交换机是否自动删除
*/
return new DirectExchange("direct_exchange_01", true, false);
}
/**
* 声明队列1
*/
@Bean
public Queue queue1() {
/*
@param1 队列的名称
@param2 队列是否持久化
*/
return new Queue("01.queue", true);
}
/**
* 声明队列2
*/
@Bean
public Queue queue2() {
return new Queue("02.queue", true);
}
/**
* 声明队列3
*/
@Bean
public Queue queue3() {
return new Queue("03.queue", true);
}
/**
* 队列1与交换机绑定
*/
@Bean
public Binding queue1Binding() {
return BindingBuilder
.bind(queue1()) // 要进行绑定的队列
.to(directExchange()) // 队列绑定的交换机
.with("1"); // 队列的路由Key(Fanout类型的交换机不需要)
}
/**
* 队列2与交换机绑定
*/
@Bean
public Binding queue2Binding() {
return BindingBuilder
.bind(queue2())
.to(directExchange())
.with("2");
}
/**
* 队列3与交换机绑定
*/
@Bean
public Binding queue3Binding() {
return BindingBuilder
.bind(queue3())
.to(directExchange())
.with("3");
}
}
2.2 简单使用案例
- 生产者
- Service接口
public interface ProducerService {
void sendMessage(String routingKey, String message);
}
- Service实现类
import com.mo.rabbitmqproducer.service.ProducerService;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class ProducerServiceImpl implements ProducerService {
@Autowired
private RabbitTemplate rabbitTemplate;
@Override
public void sendMessage(String routingKey, String message) {
// 交换机名称
String exchangeName = "direct_exchange_01";
// 发给消息队列
rabbitTemplate.convertAndSend(exchangeName, routingKey, message);
System.out.println("发送消息成功");
}
}
- 消费者
- Service接口
public interface ConsumerService {
void receiveMessage(String message);
}
- Service实现类
import com.mo.rabbitmqconsumer.service.ConsumerService;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;
@Service
@RabbitListener(queues = {"01.queue"})
public class Consumer01ServiceImpl implements ConsumerService {
@Override
@RabbitHandler
public void receiveMessage(String message) {
System.out.println("消费者01接收到消息:" + message);
}
}
import com.mo.rabbitmqconsumer.service.ConsumerService;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;
@Service
@RabbitListener(queues = {"02.queue"})
public class Consumer02ServiceImpl implements ConsumerService {
@Override
@RabbitHandler
public void receiveMessage(String message) {
System.out.println("消费者02接收到消息:" + message);
}
}
import com.mo.rabbitmqconsumer.service.ConsumerService;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;
@Service
@RabbitListener(queues = {"03.queue"})
public class Consumer03ServiceImpl implements ConsumerService {
@Override
@RabbitHandler
public void receiveMessage(String message) {
System.out.println("消费者03接收到消息:" + message);
}
}
- 测试
import com.mo.rabbitmqproducer.service.ProducerService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class RabbitmqProducerApplicationTests {
@Autowired
private ProducerService producerService;
@Test
void contextLoads() {
producerService.sendMessage("1", "Hello 01!");
producerService.sendMessage("2", "Hello 02!");
producerService.sendMessage("3", "Hello 03!");
}
}
2.3 TTL消息过期
- 可设置队列的过期时间让队列中的消息过期后删除或转移
- RabbitMQConfiguration消息队列配置类
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;
import java.util.HashMap;
import java.util.Map;
/**
* RabbitMQ配置类
*/
@Configuration
public class RabbitMQConfiguration2 {
/**
* 声明交换机
*/
@Bean
public DirectExchange ttlDirectExchange() {
/*
@param1 交换机的名称
@param2 交换机是否持久化
@param3 交换机是否自动删除
*/
return new DirectExchange("ttl_direct_exchange", true, false);
}
/**
* 声明过期队列
*/
@Bean
public Queue ttlQueue() {
// 设置过期时间
Map<String, Object> args = new HashMap<>();
args.put("x-message-ttl", 5000); // 过期时间为5000毫秒
/*
@param1 队列的名称
@param2 队列是否持久化
@param3 队列的排他性
@param4 队列是否自动删除
@param5 队列属性
*/
return new Queue("ttl.direct.queue", true, false, false, args);
}
/**
* 队列与交换机绑定
*/
@Bean
public Binding ttlQueueBinding() {
return BindingBuilder
.bind(ttlQueue()) // 要进行绑定的队列
.to(ttlDirectExchange()) // 队列绑定的交换机
.with("ttl"); // 队列的路由Key(Fanout类型的交换机不需要)
}
}
- 可设置消息的过期时间让消息到队列中过期后删除
- 设置消息过期的方法
public void ttlMessage(String routingKey, String message) {
// 交换机名称
String exchangeName = "direct_exchange_01";
// 设置消息过期时间
MessagePostProcessor messagePostProcessor = msg -> {
msg.getMessageProperties().setExpiration("5000"); // 设置过期时间为5000毫秒
return null;
};
// 发给消息队列
rabbitTemplate.convertAndSend(exchangeName, routingKey, message, messagePostProcessor);
System.out.println("发送消息成功");
}
- 同时设置了队列的过期时间和消息的过期时间时,以最短时间为准
2.4 死信队列
- 死信队列用于消息被拒绝、消息过期、队列已达最大长度时消息的接收
- RabbitMQConfiguration消息队列配置类
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;
import java.util.HashMap;
import java.util.Map;
/**
* RabbitMQ配置类
*/
@Configuration
public class RabbitMQConfiguration3 {
/**
* 声明死信交换机
*/
@Bean
public DirectExchange deadDirectExchange() {
/*
@param1 交换机的名称
@param2 交换机是否持久化
@param3 交换机是否自动删除
*/
return new DirectExchange("dead_direct_exchange", true, false);
}
/**
* 声明死信队列
*/
@Bean
public Queue deadQueue() {
return new Queue("ttl.direct.queue", true);
}
/**
* 队列与交换机绑定
*/
@Bean
public Binding deadQueueBinding() {
return BindingBuilder
.bind(deadQueue()) // 要进行绑定的队列
.to(deadDirectExchange()) // 队列绑定的交换机
.with("dead"); // 队列的路由Key(Fanout类型的交换机不需要)
}
/**
* 声明过期交换机
*/
@Bean
public DirectExchange ttlDirectExchange2() {
/*
@param1 交换机的名称
@param2 交换机是否持久化
@param3 交换机是否自动删除
*/
return new DirectExchange("ttl_direct_exchange2", true, false);
}
/**
* 声明过期队列
*/
@Bean
public Queue ttlQueue2() {
// 设置过期时间
Map<String, Object> args = new HashMap<>();
args.put("x-message-ttl", 5000); // 过期时间为5000毫秒
args.put("x-max-length", 10); // 消息队列的最大长度
args.put("x-dead-letter-exchange", "dead_direct_exchange"); // 设置死信交换机
args.put("x-dead-letter-routing-key", "dead"); // 设置路由Key(Fanout模式交换机不需要设置)
/*
@param1 队列的名称
@param2 队列是否持久化
@param3 队列的排他性
@param4 队列是否自动删除
@param5 队列属性
*/
return new Queue("ttl2.direct.queue", true, false, false, args);
}
/**
* 队列与交换机绑定
*/
@Bean
public Binding ttlQueueBinding2() {
return BindingBuilder
.bind(ttlQueue2()) // 要进行绑定的队列
.to(ttlDirectExchange2()) // 队列绑定的交换机
.with("ttl2"); // 队列的路由Key(Fanout类型的交换机不需要)
}
}
2.5 内存磁盘的预警管理控制
- 内存的预警管理控制
- 命令行的方式(重启后失效)
# 相对大小,默认方式,默认值为0.4,建议设置范围为0.4~0.7
rabbitmqctl set_vm_memory_high_watermark relative 0.5
# 绝对大小
rabbitmqctl set_vm_memory_high_watermark absolute 100MB
- 配置文件方式(重启后不失效)
vim /etc/rabbitmq/rabbitmq.conf
# 修改对应值
# 相对大小,默认方式,默认值为0.4,建议设置范围为0.4~0.7
vm_memory_high_watermark.relative = 0.5
# 绝对大小
vm_memory_high_watermark.absolute = 100MB
# 内存换页,当内存达到一个界限就将内存置换到磁盘,默认值为0.5,设置值应该小于1
vm_memory_high_watermark_paging_ratio.relative = 0.5
- 磁盘的预警管理控制
- 命令行的方式(重启后失效)
# 相对内存大小,建议设置范围为1.0~2.0
rabbitmqctl set_disk_free_limit memory_limit 1.5
# 绝对大小,默认方式,默认值为50MB
rabbitmqctl set_disk_free_limit 100MB
- 配置文件方式(重启后不失效)
vim /etc/rabbitmq/rabbitmq.conf
# 修改对应值
# 相对内存大小,建议设置范围为1.0~2.0
disk_free_limit.relative = 1.5
# 绝对大小,默认方式,默认值为50MB
disk_free_limit.absolute = 100MB
2.6 集群
- 单机集群的搭建(节点1为主节点,节点2为从节点)
# 1. 启动第一个节点
sudo RABBITMQ_NODE_PORT=5672 RABBITMQ_NODENAME=rabbit-1 rabbit-server start &
# 2. 启动第二个节点
sudo RABBITMQ_NODE_PORT=5673 RABBITMQ_SERVER_ARGS="-rabbitmq_management listener [{port,15673}]" RABBITMQ_NODENAME=rabbit-2 rabbit-server start &
# 3. 停止节点1并清除节点的历史数据,再启动
sudo rabbitmqctl -n rabbit-1 stop_app
sudo rabbitmqctl -n rabbit-1 reset
sudo rabbitmqctl -n rabbit-1 start_app
# 4. 停止节点2并清除节点的历史数据,绑定节点1再启动(Server-node为主机名或IP地址)
sudo rabbitmqctl -n rabbit-2 stop_app
sudo rabbitmqctl -n rabbit-2 reset
sudo rabbitmqctl -n rabbit-2 join_cluster rabbit-1@'Server-node'
sudo rabbitmqctl -n rabbit-2 start_app
- 节点用户添加
# 为第一、第二个节点添加用户
rabbitmqctl -n rabbit-1 add_user admin admin
rabbitmqctl -n rabbit-2 add_user admin admin
# 设置用户为管理员
rabbitmqctl -n rabbit-1 set_user_tags admin administrator
rabbitmqctl -n rabbit-2 set_user_tags admin administrator
# 为用户添加资源权限
rabbitmqctl -n rabbit-1 set_permissions -p / admin ".*" ".*" ".*"
rabbitmqctl -n rabbit-2 set_permissions -p / admin ".*" ".*" ".*"
2.7 分布式事务
- application配置
# 服务器端口
server:
port: 8081
# RabbitMQ服务配置
spring:
rabbitmq:
username: admin
password: admin
virtual-host: /
host: 192.168.150.128
port: 5672
#addresses: 192.168.150.128:5672 # 集群IP及端口连接方式
publisher-confirm-type: correlated # 消息确认机制
publisher-returns: true # 开启发送失败退回
template:
mandatory: true # 保证监听有效
listener:
simple:
acknowledge-mode: manual # 手动ACK机制
concurrency: 1 # 消费者最小数
max-concurrency: 10 # 消费者最大处
retry:
enabled: true # 支持重试/重发
- RabbitMQConfiguration消息队列配置类
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;
import java.util.HashMap;
import java.util.Map;
/**
* RabbitMQ配置类
*/
@Configuration
public class RabbitMQConfiguration4 {
/**
* 声明死信交换机
*/
@Bean
public DirectExchange deadDirectExchange2() {
/*
@param1 交换机的名称
@param2 交换机是否持久化
@param3 交换机是否自动删除
*/
return new DirectExchange("dead_direct_exchange2", true, false);
}
/**
* 声明死信队列
*/
@Bean
public Queue deadQueue2() {
return new Queue("dead2.direct.queue", true);
}
/**
* 队列与交换机绑定
*/
@Bean
public Binding deadQueueBinding2() {
return BindingBuilder
.bind(deadQueue2()) // 要进行绑定的队列
.to(deadDirectExchange2()) // 队列绑定的交换机
.with("dead2"); // 队列的路由Key(Fanout类型的交换机不需要)
}
/**
* 声明交换机
*/
@Bean
public DirectExchange directExchange2() {
/*
@param1 交换机的名称
@param2 交换机是否持久化
@param3 交换机是否自动删除
*/
return new DirectExchange("direct_exchange2", true, false);
}
/**
* 声明队列
*/
@Bean
public Queue directQueue2() {
Map<String, Object> args = new HashMap<>();
args.put("x-dead-letter-exchange", "dead_direct_exchange2"); // 设置死信交换机
args.put("x-dead-letter-routing-key", "dead2"); // 设置路由Key(Fanout模式交换机不需要设置)
/*
@param1 队列的名称
@param2 队列是否持久化
@param3 队列的排他性
@param4 队列是否自动删除
@param5 队列属性
*/
return new Queue("direct2.queue", true, false, false, args);
}
/**
* 队列与交换机绑定
*/
@Bean
public Binding directQueueBinding2() {
return BindingBuilder
.bind(directQueue2()) // 要进行绑定的队列
.to(directExchange2()) // 队列绑定的交换机
.with("direct2"); // 队列的路由Key(Fanout类型的交换机不需要)
}
}
- 生产者
- Service接口
public interface ProducerTXService {
void sendTXMessage(String message);
}
- Service实现类
import com.mo.rabbitmqproducer.service.ProducerTXService;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
@Service
public class ProducerTXServiceImpl implements ProducerTXService {
@Autowired
private RabbitTemplate rabbitTemplate;
@PostConstruct
public void regCallback() {
// 添加一个消息确认的回调方法
rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
System.out.println("cause:" + cause);
if (!ack) {
// 应答失败操作
System.out.println("MQ消息应答失败");
return;
}
try {
System.out.println("更改消息投递状态");
System.out.println("消息投递成功");
} catch (Exception e) {
System.out.println("消息状态修改失败");
System.out.println("异常操作");
}
});
}
@Override
public void sendTXMessage(String message) {
// 交换机名称
String exchangeName = "direct_exchange2";
// 路由Key
String routingKey = "direct2";
// 发给消息队列
rabbitTemplate.convertAndSend(exchangeName, routingKey, message, new CorrelationData(message));
}
}
- 消费者
- Service接口
import com.rabbitmq.client.Channel;
import org.springframework.amqp.rabbit.connection.CorrelationData;
public interface ConsumerTXService {
void receiveTXMessage(String message, Channel channel, CorrelationData correlationData, long tag);
}
- Service实现类
import com.mo.rabbitmqconsumer.service.ConsumerTXService;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Service;
import java.io.IOException;
@Service
public class ConsumerTXServiceImpl implements ConsumerTXService {
@Override
@RabbitListener(queues = {"direct2.queue"})
public void receiveTXMessage(String message, Channel channel,
CorrelationData correlationData,
@Header(AmqpHeaders.DELIVERY_TAG) long tag){
try {
// 获取消息队列的信息
System.out.println("收到的信息为:" + message);
int a = 1/0;
// 自动应答
channel.basicAck(tag, false);
} catch (Exception e) {
try {
// 手动应答
channel.basicNack(tag, false, false);
} catch (IOException ioException) {
ioException.printStackTrace();
}
}
}
}
- 测试
import com.mo.rabbitmqproducer.service.ProducerTXService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class RabbitmqProducerApplicationTests {
@Autowired
private ProducerTXService producerTXService;
@Test
void contextLoads() {
producerTXService.sendTXMessage("hello");
}
}