一、持久化

如果看到这一篇文章的朋友,都是有经验的开发人员,对持久化的概念就不用再做过多的解析了,经过前面的几篇文章,其实不难发现RabbitMQ 的持久化其实就只分交换器持久化、队列持久化和消息持久化这三个部分;

  • 定义持久化交换器,通过第三个参数 durable 开启/关闭持久化
channel.exchangeDeclare(exchangeName, exchangeType, durable)
  • 定义持久化队列,通过第二个参数 durable 开启/关闭持久化
channel.queueDeclare(queue, durable, exclusive, autoDelete, arguments);
  • 发送持久化消息,需要在消息属性中设置 deliveryMode=2 , 此属性在 BasicProperties 中,通过 basicPublish 方法的 props 参数传入。
channel.basicPublish(exchange, routingKey, props, body);
BasicProperties 对象可以从RabbitMQ 内置的 MessageProperties 类中获取
MessageProperties.PERSISTENT_TEXT_PLAIN 1
如果还需要设置其它属性,可以通过 AMQP.BasicProperties.Builder 去构建一个BasicProperties 对象;这个用法在前两篇文章中都有展示过
new AMQP.BasicProperties.Builder() .deliveryMode(2) .build()

二、持久化代码演示

/**
* 持久化示例
*/
public class Consumer {
private static Runnable receive = new Runnable() {
public void run() {
// 1、创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
// 2、设置连接属性
factory.setHost("192.168.0.1");
factory.setUsername("admin");
factory.setPassword("admin"); Connection connection = null;
Channel channel = null;
final String clientName = Thread.currentThread().getName();
String queueName = "routing_test_queue"; try {
// 3、从连接工厂获取连接
connection = factory.newConnection("消费者-" + clientName); // 4、从链接中创建通道
channel = connection.createChannel(); // 定义一个持久化的,direct类型交换器
channel.exchangeDeclare("routing_test", "direct", true);
// 定义一个持久化队列
channel.queueDeclare(queueName, true, false, false, null); // 将队列和交换器绑定,第三个参数 routingKey是关键,通过此路由键决定接收谁的消息
channel.queueBind(queueName, "routing_test", clientName); // 定义消息接收回调对象
DeliverCallback callback = new DeliverCallback() {
public void handle(String consumerTag, Delivery message) throws IOException {
System.out.println(clientName + " 收到消息:" + new String(message.getBody(), "UTF-8"));
}
};
// 监听队列
channel.basicConsume(queueName, true, callback, new CancelCallback() {
public void handle(String consumerTag) throws IOException {
}
}); System.out.println(clientName + " 开始接收消息");
System.in.read(); } catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
} finally {
// 8、关闭通道
if (channel != null && channel.isOpen()) {
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
} // 9、关闭连接
if (connection != null && connection.isOpen()) {
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}; public static void main(String[] args) {
new Thread(receive, "c1").start();
new Thread(receive, "c2").start();
}
}
public class Producer {

    public static void main(String[] args) {
// 1、创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
// 2、设置连接属性
factory.setHost("192.168.0.1");
factory.setUsername("admin");
factory.setPassword("admin"); Connection connection = null;
Channel channel = null; try {
// 3、从连接工厂获取连接
connection = factory.newConnection("生产者"); // 4、从链接中创建通道
channel = connection.createChannel(); // 定义一个持久化的,direct类型交换器
channel.exchangeDeclare("routing_test", "direct", true); // 内存、磁盘预警时用
System.out.println("按回车继续");
System.in.read(); // 消息内容
String message = "Hello A";
// 发送持久化消息到routing_test交换器上
channel.basicPublish("routing_test", "c1", MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes());
System.out.println("消息 " + message + " 已发送!"); // 消息内容
message = "Hello B";
// 发送持久化消息到routing_test交换器上
channel.basicPublish("routing_test", "c2", MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes());
System.out.println("消息 " + message + " 已发送!"); // 内存、池畔预警时用
System.out.println("按回车结束");
System.in.read();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
} finally {
// 7、关闭通道
if (channel != null && channel.isOpen()) {
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
} // 8、关闭连接
if (connection != null && connection.isOpen()) {
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

二、内存告警

默认情况下 set_vm_memory_high_watermark 的值为 0.4,即内存阈值(临界值)为 0.4,表示当RabbitMQ 使用的内存超过 40%时,就会产生内存告警并阻塞所有生产者的连接。一旦告警被解除(有消息被消费或者从内存转储到磁盘等情况的发生), 一切都会恢复正常。在出现内存告警后,所有的客户端连接都会被阻塞。阻塞分为 blocking 和 blocked 两种。
  • blocking:表示没有发送消息的链接。
  • blocked:表示试图发送消息的链接。
如果出现了内存告警,并且机器还有可用内存,可以通过命令调整内存阈值,解除告警。
rabbitmqctl set_vm_memory_high_watermark 1 1
或者
rabbitmqctl set_vm_memory_high_watermark absolute 1GB
但这种方式只是临时调整,RabbitMQ 服务重启后,会还原。如果需要永久调整,可以修改配置文件。但修改配置文件需要重启RabbitMQ 服务才能生效。

修改配置文件: vim /etc/rabbitmq/rabbitmq.conf
vm_memory_high_watermark.relative = 0.4 1
或者
vm_memory_high_watermark.absolute = 1GB

三、模拟内存告警

1. 调整内存阈值,模拟出告警,在RabbitMQ 服务器上修改。 注意:修改之前,先在管理页面看一下当前使用了多少,调成比当前值小
rabbitmqctl set_vm_memory_high_watermark absolute 50MB

2.刷新管理页面(可能需要刷新多次),在 Overview -> Nodes 中可以看到Memory变成了红色,表示此节点内存告警了

3. 启动 Producer 和 Consumer(源码链接在最下面)
4. 查看管理界面的 Connections 页面,可以看到生产者和消费者的链接都处于 blocking 状态。
5. 在 Producer 的控制台按回车健,再观察管理界面的 Connections 页面,会发现生产者的状态成了 blocked 。
6. 此时虽然在 Producer 控制台看到了发送两条消息的信息,但 Consumer 并没有收到任何消息。并且在管理界面的 Queues 页面也看到不到队列的消息数量有变化。
7. 解除内存告警后,会发现 Consumer 收到了 Producer 发送的两条消息。

四、内存换页

  • 在Broker节点的使用内存即将达到内存阈值之前,它会尝试将队列中的消息存储到磁盘以释放内存空间,这个动作叫内存换页。
  • 持久化和非持久化的消息都会被转储到磁盘中,其中持久化的消息本身就在磁盘中有一份副本,此时会将持久化的消息从内存中清除掉。
  • 默认情况下,在内存到达内存阈值的 50%时会进行换页动作。也就是说,在默认的内存阈值为 0.4的情况下,当内存超过 0.4 x 0 .5=0.2 时会进行换页动作。
  • 通过修改配置文件,调整内存换页分页阈值(不能通过命令调整)。
# 此值大于1时,相当于禁用了换页功能。
vm_memory_high_watermark_paging_ratio = 0.75

五、磁盘告警

  • 当磁盘剩余空间低于磁盘的阈值时,RabbitMQ 同样会阻塞生产者,这样可以避免因非持久化的消息持续换页而耗尽磁盘空间导致服务崩溃
  • 默认情况下,磁盘阈值为50MB,表示当磁盘剩余空间低于50MB 时会阻塞生产者并停止内存中消息的换页动作
  • 这个阈值的设置可以减小,但不能完全消除因磁盘耗尽而导致崩渍的可能性。比如在两次磁盘空间检测期间内,磁盘空间从大于50MB被耗尽到0MB
  • 通过命令可以调整磁盘阈值,临时生效,重启恢复
# disk_limit 为固定大小,单位为MB、GB
rabbitmqctl set_disk_free_limit <disk_limit>
或者
# fraction 为相对比值,建议的取值为1.0~2.0之间
rabbitmqctl set_disk_free_limit mem_relative <fraction>

其实这些内容在官网上都有说明,有兴趣可以直接看官网:https://www.rabbitmq.com/alarms.html

git源码:https://gitee.com/TongHuaShuShuoWoDeJieJu/rabbit.git

RabbitMQ持久化机制、内存磁盘控制(四)的更多相关文章

  1. 消息中间件-RabbitMQ持久化机制、内存磁盘控制

    RabbitMQ持久化机制 RabbitMQ内存控制 RabbitMQ磁盘控制 RabbitMQ持久化机制 重启之后没有持久化的消息会丢失 package com.study.rabbitmq.a13 ...

  2. RabbitMQ的持久化机制

    一.问题的引出 RabbitMQ的一大特色是消息的可靠性,那么它是如何保证消息可靠性的呢?——消息持久化.为了保证RabbitMQ在退出,服务重启或者crash等异常情况下,也不会丢失消息,我们可以将 ...

  3. 【转】RabbitMQ基础——和——持久化机制

    这里原来有一句话,触犯啦天条,被阉割!!!! 首先不去讨论我的日志组件怎么样.因为有些日志需要走网络,有的又不需要走网路,也是有性能与业务场景的多般变化在其中,就把他抛开,我们只谈消息RabbitMQ ...

  4. Redis 学习笔记(四)RDB 和 AOF 持久化机制

    一.Redis 持久化简介 Redis 的持久化功能是区别于 Memcached 显著特性,数据持久化可以保证系统在发生宕机和重启后数据不会丢失,对于 redis 这种存储在内存中的数据库显得尤为重要 ...

  5. Redis基础篇(四)持久化:内存快照(RDB)

    AOF好处是每次执行只需要记录操作命令,记录量不大.但在故障恢复时,需要逐一执行AOF的操作命令,如果日志很大,恢复就很慢. 今天学习另一种持久化方式:内存快照.内存快照,是Redis某一时刻的状态, ...

  6. Redis的持久化机制与内存管理机制

    1.概述 Redis的持久化机制有两种:RDB 和 AOF ,这两种机制有什么区别?正式环境应该采用哪种机制? 我们的服务器内存资源是有限的,如果内存被Redis的缓存占满了怎么办?这就要看Redis ...

  7. 数据库并发事务控制四:postgresql数据库的锁机制二:表锁 <转>

    在博文<数据库并发事务控制四:postgresql数据库的锁机制 > http://blog.csdn.net/beiigang/article/details/43302947 中后面提 ...

  8. 分析RedisRDB和AOF两种持久化机制的工作原理及优劣势

    一.RDB和AOF两种持久化机制的介绍 RDB持久化机制,对redis中的数据执行周期性的持久化 AOF机制对每条写入命令作为日志,以append-only(追加)的模式写入一个日志文件中,在redi ...

  9. Redis提供的持久化机制(RDB和AOF)【转载】

    Redis提供的持久化机制    Redis是一种面向“key-value”类型数据的分布式NoSQL数据库系统,具有高性能.持久存储.适应高并发应用场景等优势.它虽然起步较晚,但发展却十分迅速. 近 ...

随机推荐

  1. linux 常用命令(四)——(centos7-centos6.8)Vim安装

    centos是默认安装了vi编辑器的,vim编辑器是没安装或者未完全安装的,个人习惯用vim,所以记录一下vim编辑器的安装: 1.查看vim相关软件信息: yum search vim 2.在线安装 ...

  2. Red Hat Enterprise Linux 7.2修改主机名(hostname)

    Red Hat Enterprise Linux 7.2在安装的时候,会默认生成主机名:localhost. 那么如何修改成自己想要的自己名? //格式为:用户名@主机名 比如: [root@loca ...

  3. 干货:详解C++ sort函数的cmp参数!

    学算法的第一天你在学冒泡.桶排 在你还没搞明白快排和归并的时候 你已经学到了数据结构最后的堆排序和希尔排序 可以说排序是很多竞赛生的噩梦-- 于是它诞生了 void std::sort() Sort ...

  4. Mac OS ssh 禁用密码登陆

    $ sudo vim /etc/ssh/sshd_config PubkeyAuthentication yes PasswordAuthentication no UsePAM no then: $ ...

  5. Python之requests模块-response

    response类故名思议,它包含了服务器对http请求的响应.每次调用requests去请求之后,均会返回一个response对象,通过调用该对象,可以查看具体的响应信息. 示例如下: import ...

  6. JUC原子操作类与乐观锁CAS

    JUC原子操作类与乐观锁CAS ​ 硬件中存在并发操作的原语,从而在硬件层面提升效率.在intel的CPU中,使用cmpxchg指令.在Java发展初期,java语言是不能够利用硬件提供的这些便利来提 ...

  7. Windows下Rancher复制Pod内文件到本地

    Rancher 未提供直接获取 Pod 内文件的工具(如果有请评论告知下,蟹蟹),但提供了 Rancher 的 CLI 客户端,通过 CLI 可以调用 k8s 的 CLI (kubectl) 命令来操 ...

  8. [源码解析] 深度学习流水线并行 PipeDream(3)--- 转换模型

    [源码解析] 深度学习流水线并行 PipeDream(3)--- 转换模型 目录 [源码解析] 深度学习流水线并行 PipeDream(3)--- 转换模型 0x00 摘要 0x01 前言 1.1 改 ...

  9. javascript(2)运算符

    ### js运算符 1.运算符 1.typeof 获取当前变量类型 运算符(特殊) 2.= 赋值运算符 3.== 简要比较运算符(忽略变量的类型) 4.=== 标准比较运算符(严格变量的类型.判断是否 ...

  10. (1)java Spring Cloud+Spring boot+mybatis企业快速开发架构之微服务是什么?它的优缺点有哪些?

    ​ "微服务"一词来源于 Martin Fowler 的<Microservices>一文.微服务是一种架构风格,即将单体应用划分为小型的服务单元,微服务之间使用 HT ...