【消息队列开发】 实现内存加载

简介: 【消息队列开发】 实现内存加载

🍃前言

本次开发目标实现内存加载

我们在硬盘与内存中都存入了我们的消息,但是呢,当程序重启后,内存中的消息就会丢失,这时候我们就需要将硬盘中的数据写入内存中

🌳实现思路

首先我们定义两个变量

一个是使用一个LinkedList的数组接收硬盘里面存储的数据。

除此之外我们还定义一个变量currentOffset用来记录我们读写文件的光标

接下来我们开始进行实现,分为5步实现

🚩读取消息长度

根据我们定义存入硬盘的消息结构

先读取最前面的四个字节,这四个字节里面的内容代表的是整个消息的长度

🚩读取相应长度的消息

构造相应长度的字节数组,进行读取

并且进行判断,实际读取消息的长度是否符合

若不符合,我们需要抛出我们自定义的异常

🚩进行反序列化

🚩判定是否有效

我们需要对反序列化后的对象进行判断

若该Message对象已近无效,那么我们就可以直接跳过了

需要注意的是,这时候我们也需要将我们的currentOffset变量进行更新

🚩加入有效消息

若为有效数据, 则需要把这个 Message 对象加入到链表中. 加入之前还需要填写 offsetBeg 和 offsetEnd

进行计算 offset 的时候, 需要知道当前文件光标的位置的.

而我们的currentOffset变量正记录着我们当前的位置

🚩收尾工作

由于我们不知道消息有多长,所以我们将上述操作放入一个while(true)的循环里进行读取

但是呢,我们应该怎么判断是否读取结束呢?

其实我们使用读取四个字节的方法readInt()

当后面没有数据时,它便会抛出异常,这里我们利用这个抛出的异常,我们在最后进行捕获,但是呢。

这个抛出的“异常”其实是一个正常的数据

🚩代码实现

// 使用这个方法, 从文件中, 读取出所有的消息内容, 加载到内存中(具体来说是放到一个链表里)
// 这个方法, 准备在程序启动的时候, 进行调用.
// 这里使用一个 LinkedList, 主要目的是为了后续进行头删操作.
// 这个方法的参数, 只是一个 queueName 而不是 MSGQueue 对象. 因为这个方法不需要加锁, 只使用 queueName 就够了.
// 由于该方法是在程序启动时调用, 此时服务器还不能处理请求呢~~ 不涉及多线程操作文件.
public LinkedList<Message> loadAllMessageFromQueue(String queueName) throws IOException, MqException, ClassNotFoundException {
    LinkedList<Message> messages = new LinkedList<>();
    try (InputStream inputStream = new FileInputStream(getQueueDataPath(queueName))) {
        try (DataInputStream dataInputStream = new DataInputStream(inputStream)) {
            // 这个变量记录当前文件光标.
            long currentOffset = 0;
            // 一个文件中包含了很多消息, 此处势必要循环读取.
            while (true) {
                // 1. 读取当前消息的长度, 这里的 readInt 可能会读到文件的末尾(EOF)
                //    readInt 方法, 读到文件末尾, 会抛出 EOFException 异常. 这一点和之前的很多流对象不太一样.
                int messageSize = dataInputStream.readInt();
                // 2. 按照这个长度, 读取消息内容
                byte[] buffer = new byte[messageSize];
                int actualSize = dataInputStream.read(buffer);
                if (messageSize != actualSize) {
                    // 如果不匹配, 说明文件有问题, 格式错乱了!!
                    throw new MqException("[MessageFileManager] 文件格式错误! queueName=" + queueName);
                }
                // 3. 把这个读到的二进制数据, 反序列化回 Message 对象
                Message message = (Message) BinaryTool.fromBytes(buffer);
                // 4. 判定一下看看这个消息对象, 是不是无效对象.
                if (message.getIsValid() != 0x1) {
                    // 无效数据, 直接跳过.
                    // 虽然消息是无效数据, 但是 offset 不要忘记更新.
                    currentOffset += (4 + messageSize);
                    continue;
                }
                // 5. 有效数据, 则需要把这个 Message 对象加入到链表中. 加入之前还需要填写 offsetBeg 和 offsetEnd
                //    进行计算 offset 的时候, 需要知道当前文件光标的位置的. 由于当下使用的 DataInputStream 并不方便直接获取到文件光标位置
                //    因此就需要手动计算下文件光标.
                message.setOffsetBeg(currentOffset + 4);
                message.setOffsetEnd(currentOffset + 4 + messageSize);
                currentOffset += (4 + messageSize);
                messages.add(message);
            }
        } catch (EOFException e) {
            // 这个 catch 并非真是处理 "异常", 而是处理 "正常" 的业务逻辑. 文件读到末尾, 会被 readInt 抛出该异常.
            // 这个 catch 语句中也不需要做啥特殊的事情
            System.out.println("[MessageFileManager] 恢复 Message 数据完成!");
        }
    }
    return messages;
}

⭕总结

关于《【消息队列开发】 实现内存加载》就讲解到这儿,感谢大家的支持,欢迎各位留言交流以及批评指正,如果文章对您有帮助或者觉得作者写的还不错可以点一下关注,点赞,收藏支持一下

相关文章
|
2月前
|
缓存 监控 Java
在使用 Glide 加载 Gif 动画时避免内存泄漏的方法
【10月更文挑战第20天】在使用 Glide 加载 Gif 动画时,避免内存泄漏是非常重要的。通过及时取消加载请求、正确处理生命周期、使用弱引用、清理缓存和避免重复加载等方法,可以有效地避免内存泄漏问题。同时,定期进行监控和检测,确保应用的性能和稳定性。需要在实际开发中不断积累经验,根据具体情况灵活运用这些方法,以保障应用的良好运行。
|
3月前
|
存储 运维
.NET开发必备技巧:使用Visual Studio分析.NET Dump,快速查找程序内存泄漏问题!
.NET开发必备技巧:使用Visual Studio分析.NET Dump,快速查找程序内存泄漏问题!
|
4月前
|
Swift iOS开发
iOS开发-属性的内存管理
【8月更文挑战第12天】在iOS开发中,属性的内存管理至关重要,直接影响应用性能与稳定性。主要策略包括:`strong`(强引用),不维持对象生命期,可用于解除循环引用;`assign`(赋值),适用于基本数据类型及非指针对象属性;`copy`,复制对象而非引用,确保对象不变性。iOS采用引用计数管理内存,ARC(自动引用计数)自动处理引用增减,简化开发。为避免循环引用,可利用弱引用或Swift中的`[weak self]`。最佳实践包括:选择恰当的内存管理策略、减少不必要的强引用、及时释放不再使用的对象、注意block内存管理,并使用Xcode工具进行内存分析。
|
5月前
|
Java 运维
开发与运维内存问题之文件句柄泄漏如何解决
开发与运维内存问题之文件句柄泄漏如何解决
75 3
|
5月前
|
缓存 Java Linux
开发与运维内存问题之线上遇到故障,使用jstat命令发现Old区持续增长如何解决
开发与运维内存问题之线上遇到故障,使用jstat命令发现Old区持续增长如何解决
47 2
|
5月前
|
NoSQL Redis C++
c++开发redis module问题之在复杂的Redis模块中,特别是使用第三方库或C++开发时,接管内存统计有哪些困难
c++开发redis module问题之在复杂的Redis模块中,特别是使用第三方库或C++开发时,接管内存统计有哪些困难
|
4月前
|
Java 开发工具 Android开发
Android经典面试题之开发中常见的内存泄漏,以及如何避免和防范
本文介绍Android开发中内存泄漏的概念及其危害,并列举了四种常见泄漏原因:静态变量持有Context、非静态内部类、资源未释放及监听器未注销。提供了具体代码示例和防范措施,如使用ApplicationContext、弱引用、适时释放资源及利用工具检测泄漏。通过遵循这些建议,开发者可以有效提高应用稳定性和性能。
55 0
|
4月前
|
消息中间件 存储 网络协议
从零开始掌握进程间通信:管道、信号、消息队列、共享内存大揭秘
在操作系统中,进程间通信(IPC)是至关重要的,它提供了多种机制来实现不同进程间的数据交换和同步。本篇文章将详细介绍几种常见的IPC方式,包括管道、信号、消息队列、共享内存、信号量和套接字,帮助你深入理解并合理应用这些通信方式,提高系统性能与可靠性。
401 0
|
4月前
|
存储 编译器 C语言
【C语言篇】数据在内存中的存储(超详细)
浮点数就采⽤下⾯的规则表⽰,即指数E的真实值加上127(或1023),再将有效数字M去掉整数部分的1。
392 0
|
2月前
|
存储 C语言
数据在内存中的存储方式
本文介绍了计算机中整数和浮点数的存储方式,包括整数的原码、反码、补码,以及浮点数的IEEE754标准存储格式。同时,探讨了大小端字节序的概念及其判断方法,通过实例代码展示了这些概念的实际应用。
64 1