引言
上回说到 手写分布式存储系统v0.1版本 ,已经实现了通过监听TCP端口并将数据写到本地磁盘的功能,今天咱们就继续往上面添砖加瓦
v0.2版本大致做以下功能
-
实现滚动写文件
-
代码优化
一、滚动写文件实现
由于咱们写文件是用的mmap进行文件写入,而mmap自身原因最多只能映射到不大于2G的文件。因此在一个磁盘文件写满后,咱们需要滚动写到一个新的文件中,基本上所有分布式存储系统都是这么实现的,如kakfa、pulsar、rocketmq等等。那咱们也自己尝试实现下,大致逻辑如下
这个过程中有几个点需要考虑
- 如何判断文件写满了
- 滚动前后文件名的变化规则
第一点可以考虑在内存中维护一个整型记录当前文件的大小,否则每次写数据时判断是否写满都要去查下linux会影响性能
第二点文件名变化规则的设计方式有较多中,例如每次写新的文件名都用最新的等。参考几个系统的实现后决定采用写指定名字的文件例如 “file”,当这个文件写满1个G时,将“file”改名为“file”加当前时间如“file-20240202”,然后再新建一个名为“file”的文件进行写入。这样就能保证“file”这个文件永远都是当前正在写入的文件,核心代码如下
private boolean rollingFile() throws IOException {
preFilepath =
fileName+"-"+LocalDateTime.now().toString().replace(":","-").substring(0,19);
File preFile = new File(preFilepath);
boolean preFileExists = preFile.exists();
if (!preFileExists) {
this.fileChannel.force(false);
boolean rename = file.renameTo(preFile);
if (rename) {
this.fileChannel = new RandomAccessFile(new File(fileName), "rw")
.getChannel();
this.mappedByteBuffer = this.fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, fileSize);
WROTE_POSITION_UPDATER.set(this, 0);
return true;
} else {
LOG.error("TieredIndexFile#rollingFile: rename current file failed");
return false;
}
}
return false;
}
二、代码优化
由于v0.1版本中实现的比较莽,因此现在需要进行一个简单的重构。重构后大致逻辑可以参考下面这张不规范的UML图,首先是抽象出一个LifecycleComponent接口,由于除了网络、持久化服务之外,未来咱们可能还会有其他的服务例如监控、插件服务等等,因此咱们需要对这些服务做一层统一的抽象,所有这些服务都要提供服务启动和服务停止的接口,这样设计之后再服务启动/停止时只需要对LifecycleComponent集合列表进行统一的启动/停止操作即可,代码维护起来也很舒服。
网络方面是通过NetServiceImpl方法初始化并启动Netty引导类ServerBootstrap,ServerBootstrap启动后会监听Linux机器的网络端口,在监听到有请求时会交给ServerHandler 进行处理,在ServerHandler这里可以调用LocalDataStorageImpl方法进行数据持久化,LocalDataStorageImpl是数据持久化的统一入口,咱们针对mmap写入方式抽象并实现了DefaultMappedFile,提供了真正的mmap写磁盘操作。基本大致逻辑就是如此,尽量不做过度的设计,好的系统是演变过来的,等未来发展到一定阶段后再根据情形进行分析优化
三、功能演示
-
开发完后,咱们就可以开始进行演示了,启动服务后当在控制台看到以下信息就知道服务已经正常启动,此时就可以发数据给服务端了
-
通过指令能看到已经在目录下创建好对应的文件,由于是通过mmap方式写的数据,因此虽然咱们还没写数据到文件内,但是可以看到文件大小已经是100Byte了,这也是mmap的特点
-
通过以下指令往8888端口发送数据
(echo 'are you ok?'; sleep 2) | telnet 127.0.0.1 8888
-
通过控制台能够看到数据有写到磁盘,并且内存中维护的文件里存放数据的大小也在增加
-
重复多次第4步,可以看到日志显示已到达文件大小触发文件滚动动作
-
再看看linux文件系统可以看到,已经创建对应的文件
testWrite-2024-02-02T19-35-20
-
打印一下可以清晰的看到咱们刚刚请求的内容都被正确的持久化到磁盘中了
四、总结
上面基本上就是 v0.2 版本的内容了,不难但是你会发现使用一个分布式存储系统、看它的源码的体验,跟你自己实现一遍是完全不同的,一个现成的组件就像是一架飞机,你看得到它的机翼、发动机等等,你知道它是这样设计的;但,它为什么是这样设计的呢?那样不可以吗,这类问题恐怕会想的比较少或者虽然想了一下但是转头就忘了。但是当你自己设计去实现的过程中,你会遇到种种问题需要你去反复思考以及做取舍等等,这些都是你真正意义上成长的过程,甚至有时还会顿悟为什么那个东西人家要这样设计,这些都是无比令人振奋的事情,这不就是生命的意义吗