客户端
客户端以用户态可执行程序的形式可以运行在容器中,并且通过FUSE将挂载的卷及文件系统接口提供给其它用户态应用。
客户端缓存
客户端进程在以下几种情况下会使用客户端缓存。
客户端为了减少与资源管理节点的通信负担,会在挂载启动时获取该挂载卷中所有元数据和数据节点的地址,并且进行缓存,后续会定期从资源管理节点进行更新。
客户端为了减少与元数据节点的通信,会缓存inode,dentry以及extent元数据信息。通常意义上,读请求应该能够读到之前所有的写入,但是客户端元数据缓存可能会导致多客户端写同一个文件时的一致性问题。所以,CubeFS的设计中,不同客户端,或者说挂载点,可以同时读一个文件,但是不能够同时写一个文件。打开文件时,客户端会强制从元数据节点更新文件元数据信息。
注意
不同进程可以从同一个挂载点并发写一个文件,但是需要解决数据并发写导致的错乱问题
由于故障恢复时,raft复制组的主节点有可能发生变化,导致客户端缓存的主节点地址无效。因此,客户端在发送请求收到not leader回复时,会轮询重试该复制组的所有节点。重试成功后识别出新的主节点,客户端会缓存新的主节点地址。
对接FUSE接口
CubeFS客户端通过对接FUSE为提供用户态文件系统接口。
提示
之前,性能较低被认为是用户态文件系统最大的缺点。但是经过多年的发展,FUSE已经在性能上有了很大提高。后续,CubeFS会着手开发内核态文件系统客户端。
目前来看,FUSE的writeback cache特性并未达到预期的性能提升。
FUSE默认的写流程走的是directIO接口,使得每次写入长度较小时会有性能问题,因为每次的写请求都会被推送至后端存储。
FUSE的解决方案是writeback cache,即小写入写到缓存页即返回,由内核根据回刷策略推送至后端存储。这样,顺序的小请求会被聚合。
但是,在实际生产中,我们发现writeback cache特性作用非常有限,原因是走writecache的写操作触发了内核balance dirty page的流程,使得本应该是响应时间非常短的写操作仍然会等较长时间才返回,这个问题在小的写入时尤其明显。
客户端预热
客户端为了提高纠删卷的读取效率,可以通过预热功能将纠删码子系统的数据缓存到副本子系统中。副本子系统中的缓存内容会在预热TTL过期后,自动删除。
一级缓存(数据缓存)
L1Cache是独立于客户端的本地数据缓存服务,对外提供Put/Get/Delete接口,基于数据块(Block)进行缓存读写、淘汰操作,整体结构如下图所示。
L1Cache缓存服务为本机所有打开一级缓存配置的客户端提供缓存服务。
本地缓存数据块与远端存储数据块一一对应,并按块进行索引(BlockKey)访问,数据块索引BlockKey生成方式为:VolumeName_Inode_hex(FileOffset)
。
提示
数据块索引经过两次取模计算将内存数据块结构映射到本地缓存文件,LocalPath / hash(BlockKey)%512 / hash(BlockKey)%256 / BlockKey
。
L1CacheStoreService统一维护全局的BlockKeys,定期按照LRU进行淘汰。
L1Cache服务重启时自动扫描磁盘上的缓存数据,并重建缓存索引信息。缓存目录的增加和退出不涉及数据迁移,丢失的缓存数据重新缓存,残留的缓存数据最终会被LRU淘汰。