RabbitMQ - 高级
文章目录
24、RabbitMQ高级 - 过期时间 TTL
概述
过期时间TTL表示可以对消息设置预期的时间,在这个时间内都可以被消费者接收获取;过了之后消息将自动被删除。RabbitMQ可以消息和队列设置TTL。目前有两种方法可以设置。
- 第一种方法是通过队列属性设置,队列中所有消息都有相同的过期时间。
- 第二种方法是对消息进行单独设置,每条消息TTL可以不同。
如果上述两种方法同时使用,则消息的过期时间以两者之间TTL较小的那个数值为准。消息在队列的生存时间一旦超过设置的TTL值,就称为dead message被投递到死信队列,消费者将无法再收到该消息。
设置队列 TTL
配置类
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 org.springframework.format.annotation.NumberFormat;
import java.util.HashMap;
@Configuration
public class TTLRabbitMqConfiguration {
@Bean
public DirectExchange ttlDirectExchange(){
return new DirectExchange("ttl_direct_exchange");
}
//队列过期时间
@Bean
public Queue ttlQueue(){
//设置过期时间ttl
HashMap<String, Object> args = new HashMap<>();
args.put("x-message-ttl",5000); //这里一定是int类型
return new Queue("ttl.direct.queue",true,false,false,args);
}
@Bean
public Binding binding(){
return BindingBuilder.bind(ttlQueue()).to(ttlDirectExchange()).with("ttl");
}
}
生产者
public void makeOrderTTL(String usserid, String productid, int num) {
//1.根据商品id查询库存是否充足
//2.保存订单
String orderId = UUID.randomUUID().toString();
System.out.println("订单生成成功.....");
//3.通过MQ完成消息分发
//参数一:交换机
//参数二:路由key/queue名称
//参数三:消息内容
String exchangeName = "ttl_direct_exchange";
String routingKey = "ttl";
rabbitTemplate.convertAndSend(exchangeName, routingKey, orderId);
}
设置消息 TTL
配置
package com.ausware.yao.config;
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 org.springframework.format.annotation.NumberFormat;
import java.util.HashMap;
@Configuration
public class TTLRabbitMqConfiguration {
@Bean
public DirectExchange ttlDirectExchange(){
return new DirectExchange("ttl_direct_exchange");
}
//队列过期时间
@Bean
public Queue ttlQueue(){
//设置过期时间ttl
HashMap<String, Object> args = new HashMap<>();
args.put("x-message-ttl",5000); //这里一定是int类型
return new Queue("ttl.direct.queue",true,false,false,args);
}
//队列过期时间
@Bean
public Queue ttlMessageQueue(){
return new Queue("ttl.message.queue",true,false,false);
}
@Bean
public Binding binding(){
return BindingBuilder.bind(ttlQueue()).to(ttlDirectExchange()).with("ttl");
}
@Bean
public Binding ttlMagBinding(){
return BindingBuilder.bind(ttlMessageQueue()).to(ttlDirectExchange()).with("ttlmessage");
}
}
生产者
public void makeOrderTTLMessage(String usserid, String productid, int num) {
//1.根据商品id查询库存是否充足
//2.保存订单
String orderId = UUID.randomUUID().toString();
System.out.println("订单生成成功.....");
//3.通过MQ完成消息分发
//参数一:交换机
//参数二:路由key/queue名称
//参数三:消息内容
String exchangeName = "ttl_direct_exchange";
String routingKey = "ttlmessage";
//给消息设置过期时间
MessagePostProcessor messagePostProcessor = new MessagePostProcessor() {
@Override
public Message postProcessMessage(Message message) throws AmqpException {
//字符串
message.getMessageProperties().setExpiration("5000");
//设置编码
message.getMessageProperties().setContentEncoding("utf-8");
return message;
}
};
rabbitTemplate.convertAndSend(exchangeName, routingKey, orderId,messagePostProcessor);
}
25、RabbitMQ高级 - 死信队列
概述
DLX,全称为Dead-Letter-Exchange,可以称之为死信交换机,也有人称之为死信邮箱。当消息在一个队列中变成死信(dead message)之后,它能被重新发送到另一个交换机中,这个交换机就是DLX,绑定DLX的队列就称之为死信队列。消息变成死信,可能是由于以下的原因:
- ·消息被拒绝
- 消息过期
- 队列达到最大长度
DLX也是一个正常的交换机,和一般的交换机没有区别,它能在任何的队列上被指定,实际上就是设置某一个队列的属性。当这个队列中存在死信时,Rabbitmq就会自动地将这个消息重新发布到设置的DLX上去,进而被路由到另一个队列,即死信队列。
要想使用死信队列,只需要在定义队列的时候设置队列参数x-dead-letter-exchange指定交换机即可。
死信交换机配置
package com.ausware.yao.config;
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
@Configuration
public class DeadRabbitMQConfiguration {
//1.声明交换机
@Bean
public DirectExchange deadExchange() {
return new DirectExchange("dead_direct_exchange", true, false);
}
//2.声明队列
@Bean
public Queue deadQueue() {
return new Queue("dead.direct.queue", true);
}
@Bean
public Binding deadBanding() {
return BindingBuilder.bind(deadQueue()).to(deadExchange()).with("dead");
}
}
正常交换机配置
package com.ausware.yao.config;
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 org.springframework.format.annotation.NumberFormat;
import java.util.HashMap;
@Configuration
public class TTLRabbitMqConfiguration {
@Bean
public DirectExchange ttlDirectExchange(){
return new DirectExchange("ttl_direct_exchange");
}
//队列过期时间
@Bean
public Queue ttlQueue(){
//设置过期时间ttl
HashMap<String, Object> args = new HashMap<>();
args.put("x-message-ttl",10000); //这里一定是int类型
args.put("x-dead-letter-exchange","dead_direct_exchange");
args.put("x-dead-letter-routing-key","dead"); //fanout不需要配置
return new Queue("ttl.direct.queue",true,false,false,args);
}
//队列过期时间
@Bean
public Queue ttlMessageQueue(){
return new Queue("ttl.message.queue",true,false,false);
}
@Bean
public Binding binding(){
return BindingBuilder.bind(ttlQueue()).to(ttlDirectExchange()).with("ttl");
}
@Bean
public Binding ttlMagBinding(){
return BindingBuilder.bind(ttlMessageQueue()).to(ttlDirectExchange()).with("ttlmessage");
}
}
生产者
public void makeOrderTTL(String usserid, String productid, int num) {
//1.根据商品id查询库存是否充足
//2.保存订单
String orderId = UUID.randomUUID().toString();
System.out.println("订单生成成功.....");
//3.通过MQ完成消息分发
//参数一:交换机
//参数二:路由key/queue名称
//参数三:消息内容
String exchangeName = "ttl_direct_exchange";
String routingKey = "ttl";
rabbitTemplate.convertAndSend(exchangeName, routingKey, orderId);
}
26、RabbitMQ 内存磁盘的监控
RabbitMQde的内存警告
当内存使用超过配置的阈值或者磁盘空间剩余空间小于配置的阈值时,RabbitMQ会暂时阻塞客户端的连接,并且停止接收从客户端发来的消息,以此避免服务器的崩溃,客户端与服务端的心态检测机制也会失效。
RabbitMQ的内控制
当出现警告的时候,可以通过配置修改和调整
命令方式
rabbitmqctl set_vm_memory_high_watermark <fraction>
rabbitmqctl set_vm_memory_high_watermark absolute 50MB
配置文件方式 rabbitmq.conf
# 建议取值在0.4~0.7之间,不建议超过0.7
vm_memory_high_watermark = 0.6
# 只能是 KB/MB/GB
vm_memory_high_watermark absolute = 2GB
fraction/value为内存阈值。默认情况是:0.4/2GB,代表的含义是:当RabbitMQ的内存超过40%时,就会产生警告并且阻塞所有生产者的连接。通过此命令修改阈值在Broker重启以后将会失效,通过修改配置文件方式设置的阈值则不会随着重启而消失,但修改了配置文件一样要重启broker才会生效。
27、RabbitMQ 的集群搭建
RabbitMQ 集群
RabbitMQ这款消息队列中间件产品本身是基于Erlang编写,Erlang语言天生具备分布式特性(通过同步Erlang集群各节点的magic cookie来实现)。因此,RabbitMQ天然支持Clustering。这使得RabbitMQ本身不需要像ActiveMQ、Kafka那样通过ZooKeeper分别来实现HA方案和保存集群的元数据。集群是保证可靠性的一种方式,同时可以通过水平扩展以达到增加消息吞吐最能力的目的。
在实际使用过程中多采取多机多实例部署方式。
主要参考文档:https:/ /www.rabbitmq.com/clustering.html
集群搭建
配置的前提是你的rabbitmq可以运行起来,比如" ps aux|grep rabbitmq",你能看到相关进程,又比如运行“systemctl status rabbitmq-server”
单机多实例搭建
场景:假设有两个rabbitmq节点,分别为rabbitmq-master-1, rabbitmq-slave-1,rabbitmq-master-1作为主节点,rabbitmq-slave-1作为从节点。
启动命令∶RABBITMQ_NODE_ PORT=5672 RABBITMNODENAME=rabbitmq-master-1 rabbitmq-server -detached
结束命令: rabbitmqctl -n rabbitmq-master-1 stop
启动第一个节点 rabbit - 1
sudo RABBITMQ_NODE_PORT=5672 RABBITMQ_NODENAME=rabbitmq-master-1 rabbitmq-server start &
启动第二个节点 rabbit - 2
注意:web管理插件端口占用,所以还要指定其web插件占用的端口号
RABBITMQ_SERVER_START_ARGS=" -rabbitmq_management listener [{port,15673}]"
RABBITMQ_NODE_PORT=5673 RABBITMQ_SERVER_START_ARGS="-rabbitmq_management listener [{port,15673}]" RABBITMQ_NODENAME=rabbitmq-slave-1 rabbitmq-server start &
查看状态
ps aux|grep rabbitmq
rabbit - 1 操作为主节点
#停止应用
rabbitmqctl -n rabbitmq-master-1 stop_app
#目的是清除节点上的历史数据(如果不清除,无法将节点加入到集群)
rabbitmqctl -n rabbitmq-master-1 reset
#启动应用
rabbitmqctl -n rabbitmq-master-1 start_app
rabbit - 2 操作为从节点
#停止应用
rabbitmqctl -n rabbitmq-slave-1 stop_app
#目的是清除节点上的历史数据(如果不清除,无法将节点加入到集群)
rabbitmqctl -n rabbitmq-slave-1 reset
#将rabbit-2节点加入到rabbit-1集群中【Server-node服务器的主机名】
rabbitmqctl -n rabbitmq-slave-1 join_cluster rabbitmq-master-1@'你的主机名'
#启动应用
rabbitmqctl -n rabbitmq-slave-1 start_app
验证集群状态
rabbitmqctl cluster_status -n rabbitmq-master-1
安装图形化界面
rabbitmq-plugins enable rabbitmq_management
添加用户
rabbitmqctl -n rabbitmq-master-1 add_user admin admin
rabbitmqctl -n rabbitmq-master-1 set_user_tags admin administrator
rabbitmqctl -n rabbitmq-master-1 set_permissions -p / admin ".*" ".*" ".*"
rabbitmqctl -n rabbitmq-slaver-1 add_user admin admin
# 这里可能会有一个错误,就是admin已存在,可以忽视继续执行下一步
rabbitmqctl -n rabbitmq-slaver-1 set_user_tags admin administrator
rabbitmqctl -n rabbitmq-slaver-1 set_permissions -p / admin ".*" ".*" ".*"
访问测试
小结
如果采用多机部署方式,需读取其中一个节点的cookie,并复制到其他节点(节点之间通过cookie确定相互是否可通信)。cookie存放在/var/lib/rabbitmq/.erlang.cookie.
例如:主机名分别为rabbit-1、rabbit-2
- 1、逐个启动各节点
- 2、配百各节点的hosts文件( vim /etc/hosts)
ip1: rabbit-1
ip2: rabbit-2
其它部署雷同单机部署方式
28、RabbitMQ高级 - 分布式事务概述
简述
分布式事务指事务的操作位于不同的节点上,需要保证事务的ACID原则。
例如:在下单场景下,库存和订单如果不在同一个节点上,就涉及到分布式事务
分布式事务的方式
两阶段提交(2PC)需要数据库产商的支持,java组件有atomikosdeng
两阶段提交,通过引入协调者(Coordinator)来协调参与者的行为,并最终决定这些参与者是否要真正执行事务。
准备阶段
协调者询问参与者事务是否执行成功,参与者发回事务执行结果。
提交阶段
如果事务在每个参与者上都执行成功,事务协调者发送通知让参与者提交事务,否则,协调者发送通知让参与者回滚事务。
需要注意的是,在准备阶段,参与者执行了事务,但是还未提交。只有在提交阶段接收到协调者发来的通知后,才进行提交或者回滚.