96SEO 2026-02-19 11:18 33
消息操作并不涉及到复杂的增删改查.消息数量可能会非常多数据库访问的效率不是很高.

文件中包含若干个消息每个消息都以二进制的方式存储每个消息由两个部分构成
个字节用来保存消息的长度防止粘包问题.第二个部分为具体的二进制消息数据Message
validCount;}通过以下方法获取队列对应消息文件的路径以及队列
FileInputStream(getQueueStatPath(queueName)))
Scanner(inputStream);stat.totalCount
scanner.nextInt();stat.validCount
FileOutputStream(getQueueStatPath(queueName)))
PrintWriter(outputStream);printWriter.write(stat.totalCount
stat.validCount);printWriter.flush();}
RuntimeException(e);}}通过以下方法来创建和销毁文件和*
此方法的用处队列也是可以被删除的队列删除之后就需要调用此方法删除对应的消息文件之类的*
baseDir.getAbsolutePath());}}1.2.2、实现消息的写入
生产消息了这个消息就需要被记录到文件上(持久化的前提是文件必须要存在)*
File(getQueueDataPath(queueName));if(!queueDataFile.exists())
File(getQueueStatPath(queueName));if(!queueStatFile.exists())
将一个新的消息(message)放到队列文件中(queue)*
{//1.先检查当前文件是否存在if(!checkFilesExits(queue.getName()))
MqException([MessageFileManager]
BinaryTool.toBytes(message);//3.根据当前队列文件长度计算出
长度//这里为了避免写操作引发线程安全问题synchronized(queue)
File(getQueueDataPath(queue.getName()));message.setOffsetBeg(queueDataFile.length()
4);message.setOffsetEnd(queueDataFile.length()
FileOutputStream(queueDataFile,
DataOutputStream(outputStream))
只会写一个字节)dataOutputStream.writeInt(messageBinary.length);//写入消息体dataOutputStream.write(messageBinary);dataOutputStream.flush();}}//5.更新消息统计文件内容Stat
readStat(queue.getName());stat.validCount
随着时间的推移文件可能会越来越大并且可能存在大量的无效消息针对这种情况就需要对当前消息数据文件进行垃圾回收机制后续会讲到.
{//读写文件注意线程安全问题synchronized(queue)
RandomAccessFile(getQueueDataPath(queue.getName()),
message.getOffsetBeg())];randomAccessFile.seek(message.getOffsetBeg());randomAccessFile.read(bufferSrc);
BinaryTool.fromBytes(bufferSrc);//3.把
设置成无效diskMessage.setIsValid((byte)
对象//这个对象马上就会被从内存中删除//4.重新写入文件byte[]
BinaryTool.toBytes(diskMessage);//这里还需要将光标移动到最初这个消息的位置因为
操作也会挪动光标randomAccessFile.seek(message.getOffsetBeg());randomAccessFile.write(bufferDest);//
通过上述折腾对于文件来说只有一个字节发生改变了而已}//更新统计文件消息无效了消息个数就需要
readStat(queue.getName());if(stat.validCount
1)消息内容加载到内存中(此方法准备在程序启动的时候进行调用因此也不需要加锁)
FileInputStream(getQueueDataPath(queueName)))
dataInputStream.readInt();//2.按照长度获取消息内容byte[]
inputStream.read(buffer);//比较理论和实际消息长度if(messageSize
MqException([MessageFileManager]
BinaryTool.fromBytes(buffer);//4.判断这个消息是否是无效对象if(message.getIsValid()
messageSize);continue;}//5.有效数据就加入到链表中加入前计算一下
offsetEnd//这个位置需要知道当前文件光标的位置由于当下使用的
手动记录一下message.setOffsetBeg(currentOffset
4);message.setOffsetEnd(currentOffset
messageSize);//6.最后加入到链表当中messages.add(message);}}
业务逻辑这是为了当消息读完了能得到一个反馈(有点顺水推舟的感觉)//因为当消息读取到文件末尾readInt
就会引发异常(EOF异常)System.out.println([MessageFileManager]
具体的我们直接遍历原有的消息数据文件把所有的有效数据拷贝到一个新的文件中再把之前整个旧的文件都删除然后将新文件的名字改为旧文件的名字.
复制算法比较合适的前提是当前空间里有效的数据不多大部分是无效的数据减少搬运数据的开销
readStat(queueName);if(stat.totalCount
这个过程中其他线程不得干预synchronized(queue)
System.currentTimeMillis();//1.创建一个新文件File
File(getQueueDataNewPath(queue.getName()));if(queueDataNewFile.exists())
MqException([MessageFileManager]
queueDataNewFile.createNewFile();if(!ok)
MqException([MessageFileManager]
queueDataNewFile.getName());}//2.从旧文件中读出所有的有效消息LinkedListMessage
loadAllMessageFromQueue(queue.getName());//3.把有效消息写入新的文件try(OutputStream
FileOutputStream(queueDataNewFile))
DataOutputStream(outputStream))
BinaryTool.toBytes(message);//先写消息长度dataOutputStream.writeInt(buffer.length);//再写消息内容dataOutputStream.write(buffer);}}}//4.删除旧文件File
File(getQueueDataPath(queue.getName()));ok
queueDataOldFile.delete();if(!ok)
MqException([MessageFileManager]
queueDataOldFile.getName());}//把
queueDataNewFile.renameTo(queueDataOldFile);if(!ok)
MqException([MessageFileManager]
queueDataNewFile.getAbsolutePath()
queueDataOldFile.getAbsolutePath());}//5.跟新统计文件Stat
readStat(queue.getName());stat.totalCount
messages.size();stat.validCount
messages.size();writeStat(queue.getName(),
System.currentTimeMillis();System.out.println([MessageFileManager]
当某个队列中消息特别多并且很多都是有效的消息就会导致后续对这个文件操作的成本上升很多例如文件大小是
文件拆分当单个文件长度到达一定阈值以后就会拆分成两个文件.拆着拆着就成了很多文件.文件合并每个单独的文件都会进行
需要专门的数据结构来存储当前队列中有多少个数据文件每个文件大小是多少消息数目是多少无效消息是多少.设计策略什么时候触发消息拆分什么时候触发文件合并.
Ps这里可以先不给出具体实现需要的可以私信我前提是备注微信号.
上层逻辑需要操作硬盘统一通过这个类来操作(上层代码不关心当前数据是存储再数据库还是文件中的)提高了代码的内聚可维护性.
DataBaseManager();//这个实例用来管理数据文件中的数据private
是一个空方法只是先列在这里一旦后续需要扩展就在这里进行初始化即可messageFileManager.init();}//封装交换机操作public
{dataBaseManager.insertExchange(exchange);}public
{dataBaseManager.deleteExchange(exchangeName);}public
dataBaseManager.selectAllExchanges();}//封装队列操作public
{dataBaseManager.insertQueue(queue);//创建队列的同时不仅需要把队列写入到数据库中还需要创建出对应的删除messageFileManager.destroyQueueFiles(queueName);}public
dataBaseManager.selectAllQueues();}//封装绑定操作public
{dataBaseManager.insertBinding(binding);}public
{dataBaseManager.deleteBinding(binding);}public
dataBaseManager.selectAllBindings();}//封装消息操作public
{messageFileManager.sendMessage(queue,
{messageFileManager.deleteMessage(queue,
message);//这里删除消息以后还需要看以下文件中是否有太多的无效文件需要进行清除if(messageFileManager.checkGC(queue.getName()))
{messageFileManager.gc(queue);}}public
selectAllMessagesFromQueue(String
messageFileManager.loadAllMessageFromQueue(queueName);}}Ps这里对队列和消息的封装都是具有一定的逻辑的
创建队列的同时不仅需要把队列写入到数据库中还需要创建出对应的删除
删除消息以后还需要看以下文件中是否有太多的无效文件需要进行清除GC
作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。
| 服务项目 | 基础套餐 | 标准套餐 | 高级定制 |
|---|---|---|---|
| 关键词优化数量 | 10-20个核心词 | 30-50个核心词+长尾词 | 80-150个全方位覆盖 |
| 内容优化 | 基础页面优化 | 全站内容优化+每月5篇原创 | 个性化内容策略+每月15篇原创 |
| 技术SEO | 基本技术检查 | 全面技术优化+移动适配 | 深度技术重构+性能优化 |
| 外链建设 | 每月5-10条 | 每月20-30条高质量外链 | 每月50+条多渠道外链 |
| 数据报告 | 月度基础报告 | 双周详细报告+分析 | 每周深度报告+策略调整 |
| 效果保障 | 3-6个月见效 | 2-4个月见效 | 1-3个月快速见效 |
我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:
全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。
基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。
解决网站技术问题,优化网站结构,提升页面速度和移动端体验。
创作高质量原创内容,优化现有页面,建立内容更新机制。
获取高质量外部链接,建立品牌在线影响力,提升网站权威度。
持续监控排名、流量和转化数据,根据效果调整优化策略。
基于我们服务的客户数据统计,平均优化效果如下:
我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。
Demand feedback