想搭建个NAS存资料,但自建NAS有一个问题:从公网访问的体验很差。原因有三:一是不支持文件随机读写;二是不能自定义缓存策略;三是不能和操作系统紧密结合

有时候我出门在外,想访问家里NAS上的一个巨型压缩包,从里面解压一个文件出来。要么只能把压缩包完整下载后再解压,要么需要用ssh连回家远程解压。无论是哪一种方法操作起来都很麻烦,要么费时间,要么费流量。

再者是不能像电脑上的本地磁盘一样,无缝访问NAS里的文件。我习惯使用Typroa记笔记。但Typroa只能读取本地文件,没法读取webdav等网盘上的文件。虽然Windows有SMB协议和自带的webdav的支持,但体验并不好,不是卡就是慢。

我也考虑过三方的webdav/ftp挂载软件,但也都不好用。这些软件大多都不开源,且带有广告。而且具体的文件缓存策略无法查看也无法调整。

首先说webdav和ftp这些通用协议,这些协议在三方软件的挂载下,使用体验确实要比Windows挂载要好。但是这些协议对高并发都不友好,ftp无法支持并发访问,webdav支持并发,但却因为http协议头很大,传输和编码解码都会带来显著的延迟。且webdav对随机写入好像支持的也不够好。

然后是SMB协议,也就是Windows网络共享,SMB性能很好,但它只能用于局域网,再公网部署会非常麻烦,一方面是安全性问题,另一方面是公网普遍有延迟。SMB在高延迟的环境下性能并不乐观,甚至可以说很差。

最后,我决定自己整一个网盘挂载方案。

做虚拟磁盘需要写Windows驱动,我看了一下教程就放弃了,太难了。不过好在我找到一个叫dokany的库可以帮我帮我做自己的文件系统。

这个库虽然牛逼,但它也挺难用的。它的文档只有一页,然后就没了!遇到问题只能翻源码。其次它入门的那个示例代码行数高达2000行的,而且还没什么注释。经常就是莫名其妙多出一个函数调用,而且光看名字还不知道它是干嘛的,去Google搜还搜不到。好家伙,我搁这解密呢?反正就是非常地劝退。

就这样硬啃了半年左右,虽然做出了demo,但我又发现这个库有隐藏bug,并且还不能稳定复现,搞得你不知道究竟是你代码的问题,还是它库的问题,非常地难排查。

我又不停地想办法。好在后来发现了一个叫winfsp的库,功能和dokany是一样的。但是它的文档和教程要比dokany好用一万倍!!!甚至还有好长的一篇超详细的教程带你入门winfsp开发。每一个函数为什么要这样写,这样写有什么作用,什么时候会调用这个函数。讲解地是非常的清楚,真的爱了。考虑再三我决定换成winfsp。说实话换库的成本很高,基本上相当于重写了一次,因为之前做的东西全部废掉了。

后来经过一年半断断续续的开发,我的文件系统已经初具雏形了,现在已经可以达到基本能正常使用的状态了。而且读写速度都还不错:

  • 写操作:150 mb/s
  • 读操作(无缓存):150 mb/s
  • 读操作(有缓存):2.3 Gb/s

perf.png

上面的测试都是单线程的顺序性能,文件系统本身是支持高并发的,实际情况会更好。至于4k性能,在有缓的情况下,4k性能可以超过本地硬盘(毕竟RAM就是快)。无缓的情况下取决于服务端的磁盘性能,网络损失应该在10%以内。

我用的是私有二进制协议进行通信,在并发和缓存的加持下,最后性能还不错,在200毫秒的高网络延迟下,资源管理器大约有1s左右的延迟,正常使用应该是没问题的(文件系统对延迟可太敏感了,甚至超过带宽)。

这些测试是在Debug模式进行测试的,如果切换到Release模式性能还能再提升。

文件系统本身的资源占用也是相当少,本身占用大约4Mb左右内存,每缓存一个文件的元数据(包括文件时间,大小,名称,安全描述符,属性)大约要300个字节左右的内存。缓存10万个文件元数据大约需要40mb内存(不考虑内存碎片化带来的额外内存分配)。

文件的二进制数据和元数据分开缓存,当二进制数据的总缓存量超过一个限制时(比如1GB),会自动回收最长时间没有被访问过的缓存,使整体的RAM使用量在一个范围内。

服务端RAM占用5Mb左右。用最低配置256mb内存的树莓派1也能轻松带起。

我想在香橙派Zero2上搭建这个文件系统,先临时用着,后面再换x86小主机来跑。(主要是没钱

到时候可以买个USB HUB,把我的硬盘都挂载到NAS上去,然后用网络将NAS和电脑连接,这样不用占用电脑上少的可怜的USB接口了。

得益于对弱网环境的优化,即使出门在外,也可以远程挂载到家里的NAS上,无论是查找文件,还是看电影,都非常的合适。

后面如果有多余的时间精力的话,还会考虑最一下安卓的App,我还是挺想在手机上也能访问的。