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

🍃前言

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

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

🌳实现思路

首先我们定义两个变量

一个是使用一个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;
}

⭕总结

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

  • 15
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

遇事问春风乄

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值