这是我参与「第五届青训营 」伴学笔记创作活动的第 8 天
消息队列
分布式中间件
- ActiveMQ
- RibbitMQ:支持事务,容错性较高,支持模式多
- Kafka:基于tcp/ip二进制协议,性能最高,不支持事务
- RocketMQ
场景
- 消息中间件监控数据
- 异步数据传输
- 任务调度
- 海量数据同步场景
- 分布式事务场景
- 大数据分析
协议
- AMQP
- MQTT
- 持久化设计
- kafka协议
- 消息分发设计
- 高可用设计
- 可靠性设计
- 容错设计
负载均衡中间件
- Nginx
- LVS负载均衡软件
- KeepAlive
- CDN
缓存中间件
- memcache
- redis
数据库中间件
- mycat
- shardingJdbc
本质
负责数据的传接收、存储和传递,接收数据,接收请求,存储数据,发送数据等功能的技术服务。
核心组成部分
-
消息的协议:计算机底层操作系统和app通讯共同遵守的约定
- rabbitMQ遵守的AMQP协议(建立在tcp/ip协议上)
-
消息的持久化机制
- 将数据存入磁盘,而不是存在内存中随服务器重启断开而消失
- 持久化方式:文件存储、数据库
-
消息的分发策略
- 发布订阅
- 轮询分发:所有的服务器不论性能都是一样的
- 公平分发:能力高多分发,能力低少分发
- 重发
- 消息拉取
-
消息的高可用、高可靠
- 高可用:指产品在规定的条件和时间内处于可执行规定功能状态的能力,从是否能集群的部署考虑
- 高可靠:系统无故障低持续运行,如系统异常不影响线上业务员的正常运行,从系统持久化方面考虑
-
消息的容错机制
面试题:为什么消息中间件不直接使用http协议?
因为http请求头复杂,对于一个消息中间件不需要这么复杂,就是负责数据传递、存储、分发;并且http是短连接(不存储),会造成消息的丢失。
AMQP协议
高级消息队列协议,采用Erlang语言实现
特性:
-
分布式事务支持
-
消息的持久化支持
-
高性能高可靠消息处理优势
-
Advanced Message Queuing Protocol:高级消息队列
- 创建连接
- 开启通道
- 发送消息
- 释放资源
MQTT协议
特点:
- 轻量
- 结构简单
- 传输块,不支持事务
- 没有持久化设计
OpenMessage协s议
如RocketMQ使用
特点:
- 结构简单
- 解析速度块
- 支持事务和持久化设计
Kafka协议
基于tcp/ip的二进制协议,消息内容通过长度来分割,由一些基本数据类型组成
特点:
- 结构简单
- 解析速度块
- 无事务支持
- 支持持久化设计
RibbitMQ消息队列
安装
Downloading and Installing RabbitMQ — RabbitMQ
Erlang Solutions - Scalable Distributed Technology (erlang-solutions.com)
环境部署:Erlang+rabbitmq
直接地址:
rabbitmq:pan.baidu.com/s/1aJH6ibJH…
提取码:ujje
erlang:pan.baidu.com/s/1sdhwdL6-…
提取码:udqn
1.安装erlang
配置环境变量
在Path中配置
%ERLANG_HOME%\bin
cmd验证是否安装成功
2.安装rabbitMQ
打开RabbitMQ的sbin目录
D:\Environment\RabbitMQ-3.7.4\rabbitmq_server-3.7.4\sbin>
输入以下命令安装插件
rabbitmq-plugins enable rabbitmq_management
输入命令查看是否安装成功
rabbitmqctl status
打开浏览器,输入网址访问管理界面的登录页面
http://127.0.0.1:15672
用户名和密码都是guest
问题:
出现 Authentication failed (rejected by the remote node), please check the Erlang cookie
修改cookie改成一样的即可
- C:\Windows\System32\config\systemprofile.erlang.cookie
- C:\Users{{电脑用户名}}.erlang.cookie
如果浏览器打不开登录页面
- 输入
rabbitmq-plugins enable rabbitmq_management开启管理界面 - 检查erlang环境
授权账号和密码
新增用户
rabbitmqctl add_user admin admin
设置用户分配操作权限
rabbitmqctl set_user_tags admin administrator
用户级别:
- administrator可以登录控制台、查看所有信息,对mq进行管理
- monitoring 监控者 登录控制台,查看所有信息
- policymaker 策略制定者 登录控制台 指定策略
- managment 普通管理员 登录控制台
为用户添加资源权限
rabbitmqctl.bat set_permissions -p / admin ".*" ".*" ".*"
rabbitmqctl change_password Username Newpassword
rabbitmqctl delete_user
rabbitmqctl list_users #查看用户清单
入门案例
应用场景:高内聚、低耦合;派单、抢票,流量监控,日志监控等
创建一个maven工程项目
导入rabbitmq的maven依赖
java原生依赖
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.10.0</version>
</dependency>
Provider
package com.liu.rabbitmq.simple;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
public class Producer {
public static void main(String[] args) {
//所有的中间件都是基于tcp、ip协议之上的新协议,rabbitmq遵循amqp
//1.创建连接工程
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(15672);
connectionFactory.setUsername("admin");
connectionFactory.setPassword("admin");
connectionFactory.setVirtualHost("/");
Connection connection = null;
Channel channel = null;
try {
// 2.创建连接connection
connection = connectionFactory.newConnection("生产者");
// 3.通过连接获取通道channel
channel = connection.createChannel();
//4.通过创建交换机,声明队列,绑定关系,路由key,发送消息,接收消息
String queryName = "queue1";
//声明队列 参数:队列的名称 是否要持久化 排他性是否是独占队列 是否自动删除队列 携带附加参数
channel.queueDeclare(queryName,false,false,false,null);
// 5.准备消息内容
String message = "Hello liuxiang!";
// 6.发送消息给队列queue 第一个参数代表默认的交换机,交换机不能没有
channel.basicPublish("",queryName,null,message.getBytes());
System.out.println("消息发送成功");
} catch (Exception ex) {
ex.printStackTrace();
} finally {
if (channel !=null && channel.isOpen()){
try {
//8.关闭通道
channel.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
// 7.关闭连接
if (connection !=null && connection.isOpen()){
try {
connection.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
}
Consumer
package com.liu.rabbitmq.simple;
import com.rabbitmq.client.*;
import java.io.IOException;
public class Consumer {
public static void main(String[] args) {
//所有的中间件都是基于tcp、ip协议之上的新协议,rabbitmq遵循amqp
//1.创建连接工程
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setUsername("admin");
connectionFactory.setPassword("admin");
connectionFactory.setVirtualHost("/");
Connection connection = null;
Channel channel = null;
try {
// 2.创建连接connection
connection = connectionFactory.newConnection("生产者");
// 3.通过连接获取通道channel
channel = connection.createChannel();
//4.通过创建交换机,声明队列,绑定关系,路由key,发送消息,接收消息
channel.basicConsume("queue1", true, new DeliverCallback() {
public void handle(String consumerTag, Delivery message) throws IOException {
System.out.println("收到消息是"+ new String(message.getBody(),"UTF-8"));
}
},new CancelCallback(){
public void handle(String consmerTag) throws IOException{
System.out.println("接收失败");
}
});
System.out.println("开始接收消息");
System.in.read();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
if (channel !=null && channel.isOpen()){
try {
//8.关闭通道
channel.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
// 7.关闭连接
if (connection !=null && connection.isOpen()){
try {
connection.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
}
结果:
问题:如果报错IO异常
在rabbitMQ管理界面设置允许即可!
让Admin变成可接入状态
RabbitMQ模式
默认模式是direct模式
headers模式:携带参数
简单模式
通过交换机推送消息到队列,消费者监听队列;绑定的路由key就是队列的名字
发布订阅模式(fanout)
声明队列绑定交换机
发送消息
队列接收消息
代码模式理解
Provider
public static void main(String[] args) {
//所有的中间件都是基于tcp、ip协议之上的新协议,rabbitmq遵循amqp
//1.创建连接工程
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setUsername("admin");
connectionFactory.setPassword("admin");
connectionFactory.setVirtualHost("/");
Connection connection = null;
Channel channel = null;
try {
// 2.创建连接connection
connection = connectionFactory.newConnection("生产者");
// 3.通过连接获取通道channel
channel = connection.createChannel();
//4.通过创建交换机,声明队列,绑定关系,路由key,发送消息,接收消息
String queryName = "queue1";
//声明队列 参数:队列的名称 是否要持久化 排他性是否是独占队列 是否自动删除队列 携带附加参数
channel.queueDeclare(queryName,false,false,false,null);
// 5.准备消息内容
String message = "Hello liuxiang!";
// 6.准备交换机
String exchangeName = "fanout-exchange";
//7.定义路由key
String routeKey = "";
//8.交换机的类型
String type = "fanout";
channel.basicPublish(exchangeName, routeKey,null,message.getBytes());
System.out.println("消息发送成功");
} catch (Exception ex) {
ex.printStackTrace();
} finally {
if (channel !=null && channel.isOpen()){
try {
//8.关闭通道
channel.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
// 7.关闭连接
if (connection !=null && connection.isOpen()){
try {
connection.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
Consumer
private static Runnable runnable = new Runnable() {
@Override
public void run() {
//所有的中间件都是基于tcp、ip协议之上的新协议,rabbitmq遵循amqp
//1.创建连接工程
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setUsername("admin");
connectionFactory.setPassword("admin");
connectionFactory.setVirtualHost("/");
//获取队列的名称
final String queueName = Thread.currentThread().getName();
Connection connection = null;
Channel channel = null;
try {
// 2.创建连接connection
connection = connectionFactory.newConnection("生产者");
// 3.通过连接获取通道channel
channel = connection.createChannel();
//4.通过创建交换机,声明队列,绑定关系,路由key,发送消息,接收消息
channel.basicConsume(queueName, true, new DeliverCallback() {
public void handle(String consumerTag, Delivery message) throws IOException {
System.out.println(queueName+":收到消息是"+ new String(message.getBody(),"UTF-8"));
}
},new CancelCallback(){
public void handle(String consmerTag) throws IOException{
System.out.println("接收失败");
}
});
System.out.println("开始接收消息");
System.in.read();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
if (channel !=null && channel.isOpen()){
try {
//8.关闭通道
channel.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
// 7.关闭连接
if (connection !=null && connection.isOpen()){
try {
connection.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
} ;
public static void main(String[] args) {
new Thread(runnable,"queue1").start();
new Thread(runnable,"queue2").start();
new Thread(runnable,"queue3").start();
}
Routing模式(direct)
在发布订阅模式上增加了路由key,相当于指定发送消息
主题模式(topic)
进行模糊路由
交换机 #:可以是0级
代码模式:只需要替换上面代码三步
// 6.准备交换机
String exchangeName = "topic-exchange";
//7.定义路由key
String routeKey = "com.order.text.xxx";
//8.交换机的类型
String type = "topic";
完整代码
Provider
public static void main(String[] args) {
//所有的中间件都是基于tcp、ip协议之上的新协议,rabbitmq遵循amqp
//1.创建连接工程
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setUsername("admin");
connectionFactory.setPassword("admin");
connectionFactory.setVirtualHost("/");
Connection connection = null;
Channel channel = null;
try {
// 2.创建连接connection
connection = connectionFactory.newConnection("生产者");
// 3.通过连接获取通道channel
channel = connection.createChannel();
// 4.准备消息内容
String message = "Hello liuxiang!";
// 5.准备交换机
String exchangeName = "direct-message-exchange";
//6.定义路由key
String routeKey = "course";
//7.交换机的类型
String type = "direct";
//8 声明交换机 持久化就是交换机不会随着服务器重启而丢失 true代表不丢失
channel.exchangeDeclare(exchangeName,type,true);
//声明队列
channel.queueDeclare("queue5",true,false,false,null);
channel.queueDeclare("queue6",true,false,false,null);
channel.queueDeclare("queue7",true,false,false,null);
//绑定队列和交换机关系
channel.queueBind("queue5",exchangeName,"order");
channel.queueBind("queue6",exchangeName,"order");
channel.queueBind("queue7",exchangeName,"course");
channel.basicPublish(exchangeName, routeKey,null,message.getBytes());
System.out.println("消息发送成功");
} catch (Exception ex) {
ex.printStackTrace();
System.out.println("发送消息出现异常");
} finally {
if (channel !=null && channel.isOpen()){
try {
//8.关闭通道
channel.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
// 7.关闭连接
if (connection !=null && connection.isOpen()){
try {
connection.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
Consumer
private static Runnable runnable = new Runnable() {
@Override
public void run() {
//所有的中间件都是基于tcp、ip协议之上的新协议,rabbitmq遵循amqp
//1.创建连接工程
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setUsername("admin");
connectionFactory.setPassword("admin");
connectionFactory.setVirtualHost("/");
//获取队列的名称
final String queueName = Thread.currentThread().getName();
Connection connection = null;
Channel channel = null;
try {
// 2.创建连接connection
connection = connectionFactory.newConnection("生产者");
// 3.通过连接获取通道channel
channel = connection.createChannel();
//4.通过创建交换机,声明队列,绑定关系,路由key,发送消息,接收消息
channel.basicConsume(queueName, true, new DeliverCallback() {
public void handle(String consumerTag, Delivery message) throws IOException {
System.out.println(queueName+":收到消息是"+ new String(message.getBody(),"UTF-8"));
}
},new CancelCallback(){
public void handle(String consmerTag) throws IOException{
System.out.println("接收失败");
}
});
System.out.println(queueName+"开始接收消息");
System.in.read();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
if (channel !=null && channel.isOpen()){
try {
//8.关闭通道
channel.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
// 7.关闭连接
if (connection !=null && connection.isOpen()){
try {
connection.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
} ;
public static void main(String[] args) {
new Thread(runnable,"queue5").start();
new Thread(runnable,"queue6").start();
new Thread(runnable,"queue7").start();
}
工作队列模式(work)
轮询分发(Round-Robin)
一个消费者一条,按均分配
Producer
public static void main(String[] args) {
//所有的中间件都是基于tcp、ip协议之上的新协议,rabbitmq遵循amqp
//1.创建连接工程
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setUsername("admin");
connectionFactory.setPassword("admin");
connectionFactory.setVirtualHost("/");
Connection connection = null;
Channel channel = null;
try {
// 2.创建连接connection
connection = connectionFactory.newConnection("生产者");
// 3.通过连接获取通道channel
channel = connection.createChannel();
// 4.准备消息内容
for (int i = 1; i <=20 ; i++) {
String message = "刘想:"+ "\t" + i;
channel.basicPublish("", "queue1",null,message.getBytes());
Thread.sleep(1000);
}
System.out.println("消息发送成功");
} catch (Exception ex) {
ex.printStackTrace();
System.out.println("发送消息出现异常");
} finally {
if (channel !=null && channel.isOpen()){
try {
//8.关闭通道
channel.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
// 7.关闭连接
if (connection !=null && connection.isOpen()){
try {
connection.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
Work1
private static Runnable runnable = new Runnable() {
@Override
public void run() {
//所有的中间件都是基于tcp、ip协议之上的新协议,rabbitmq遵循amqp
//1.创建连接工程
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setUsername("admin");
connectionFactory.setPassword("admin");
connectionFactory.setVirtualHost("/");
Connection connection = null;
Channel channel = null;
try {
// 2.创建连接connection
connection = connectionFactory.newConnection("生产者");
// 3.通过连接获取通道channel
channel = connection.createChannel();
// channel.basicQos(1);
//4.通过创建交换机,声明队列,绑定关系,路由key,发送消息,接收消息
channel.basicConsume("queue1", true, new DeliverCallback() {
@Override
public void handle(String consumerTag, Delivery message) throws IOException {
System.out.println("Work1"+":收到消息是"+ new String(message.getBody(),"UTF-8"));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},new CancelCallback(){
@Override
public void handle(String consmerTag) throws IOException{
System.out.println("接收失败");
}
});
System.out.println("Work1"+"开始接收消息");
System.in.read();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
if (channel !=null && channel.isOpen()){
try {
//8.关闭通道
channel.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
// 7.关闭连接
if (connection !=null && connection.isOpen()){
try {
connection.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
} ;
public static void main(String[] args) {
new Thread(runnable,"queue1").start();
}
Work2与Work1代码一致
结果:都是公平的!
公平分发
根据消费者的消费能力进行公平分发,处理快的处理多,能者多劳
修改这一部分代码:改为手动应答即可!
//4.通过创建交换机,声明队列,绑定关系,路由key,发送消息,接收消息
finalChannel.basicConsume("queue1", false, new DeliverCallback() {
@Override
public void handle(String consumerTag, Delivery message) throws IOException {
try {
System.out.println("Work1"+":收到消息是"+ new String(message.getBody(),"UTF-8"));
Thread.sleep(1000);
//改为手动应答
finalChannel.basicAck(message.getEnvelope().getDeliveryTag(),false);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
SpringBoot整合RabbitMQ
Provider
1.导入依赖
<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>
2.rabbitMQ的配置文件
在本机上不用配置,再真实服务器上需要配置
#服务端口
server:
port: 8080
#配置rabbitmq服务
spring:
rabbitmq:
username: admin
password: admin
host: 127.0.0.1
virtual-host: /
port: 5672
3.服务层
@Service
public class OrderService {
@Autowired(required = false)
private RabbitTemplate rabbitTemplate;
public void makeOrder(String userid,String productid,int num){
//1.根据商品id查询库存是否充足
//2.保存订单
String orderId = UUID.randomUUID().toString();
System.out.println("订单生成成功:"+orderId);
//3.通过消息队列完成消息的分发
//参数1:交换机 2:路由key 3:消息内容
String exchangeName = "fanout_order_exchange";
String routingKey = "";
rabbitTemplate.convertAndSend(exchangeName,routingKey,orderId);
}
}
4.rabbitMQ配置类
@Configuration
public class RabbitMqConfiguration {
//声明注册fanout模式的交换机
@Bean
public FanoutExchange fanoutExchange(){
return new FanoutExchange("fanout_order_exchange",true,false);
}
//声明队列
@Bean
public Queue smsQueue(){
return new Queue("sms.fanout.queue",true);
}
@Bean
public Queue messageQueue(){
return new Queue("message.fanout.queue",true);
}
@Bean
public Queue emailQueue(){
return new Queue("email.fanout.queue",true);
}
//完成绑定关系
@Bean
public Binding msmBinding(){
return BindingBuilder.bind(smsQueue()).to(fanoutExchange());
}
@Bean
public Binding messageBinding(){
return BindingBuilder.bind(messageQueue()).to(fanoutExchange());
}
@Bean
public Binding emailBinding(){
return BindingBuilder.bind(emailQueue()).to(fanoutExchange());
}
}
5.结果
Consumer
1.配置文件
#服务端口
server:
port: 8081
#配置rabbitmq服务
spring:
rabbitmq:
username: admin
password: admin
host: 127.0.0.1
virtual-host: /
port: 5672
2.service
@Service
@RabbitListener(queues = {"sms.fanout.queue"})
public class FanoutSmsConsumer {
@RabbitHandler
public void receiveMessage(String message){
System.out.println("sms fanout==>接收了订单信息:"+message);
}
}
@Service
@RabbitListener(queues = {"message.fanout.queue"})
public class FanoutMessageConsumer {
@RabbitHandler
public void receiveMessage(String message){
System.out.println("message fanout==>接收了订单信息:"+message);
}
}
@Service
@RabbitListener(queues = {"email.fanout.queue"})
public class FanoutEmailConsumer {
@RabbitHandler
public void receiveMessage(String message){
System.out.println("email fanout==>接收了订单信息:"+message);
}
}
注解方式实现
@RabbitListener(bindings = @QueueBinding(
value = @Queue(value = "sms.topic.queue",durable = "true",autoDelete = "false"), exchange = @Exchange(value = "topic_order_exchange",type=ExchangeTypes.TOPIC),key = "#.sms.#"
))
RabbitMQ高级
过期时间TTL
表示对消息设置预期的时间,在这个时间内都可以被消费者接收获取,过了之后消息自动删除。
队列参数设置
在配置类中声明
//声明队列
@Bean
public Queue directTtlQueue(){
Map<String,Object> map = new HashMap<>();
map.put("x-message-ttl",5000);//参数 设置5s过期
return new Queue("ttl.direct.queue",true,false,map);
}
消息设置
在service中声明
MessagePostProcessor messagePostProcessor = new MessagePostProcessor(){
@Override
public Message postProcessMessage{Message message} throws AmqpException{
message.getMessageProperties().setExpiration("5000");
message.getMessageProperties().setContentEncoding("UTF-8");
return message;
}
}
两者同时设置以最短的设置为准!
死信队列(DLX)
当一个消息在一个队列中变成死信队列后,它能被重新发送到另一个交换机中,这个交换机就是DLX,绑定DLX的队列就是死信队列。
配置文件
//声明队列
@Bean
public Queue directTtlQueue(){
Map<String,Object> map = new HashMap<>();
map.put("x-message-ttl",5000);//参数 设置5s过期
map.put("x-dead-letter-exchange","dead_direct_exchange");
map.put("x-dead-letter-routing-key","dead");//如果fanout模式不用设置
return new Queue("ttl.direct.queue",true,false,map);
}
内存磁盘的监控
命令方式:默认是2GB/0.4,随着重启会丢失
低于2GB就会阻塞!
rabbitmqctl set_vm_memory_high_watermark absolute 2GB
rabbitmqctl set_vm_memory_high_watermark relative 0.6 #0.4-0.7之间
磁盘预警
rabbitmqctl set_disk_free_limit memory_limit <fraction> #1.0-2.0之间
内存换页
rabbitmqctl set_vm_memory_high_watermark_paging_ratio 0.5
思考:分布式事务问题,如何保证数据的一致性?
通过消息中间件来解决:可靠生产+可靠消费 (ack机制)
消息确认机制配置:
rabbitmq:
publisher-confirm-type: correlated
解决可靠消费中消息重试的集中方案:
- 控制重发次数
- try+catch+手动ack
- try+catch+手动ack+死信队列处理+人工干预
配置
listener:
simple:
acknowledge-mode: manual #达到次数后,不丢失消息 手动ack
retry:
enabled: true #开启重试
manx-attempts: 3 #最大重试次数
initial-interval: 2000ms #重试间隔时间