- Website: https://github.com/mumu-architect/QFS
- Chat: Gitter
- QFS 是一个开源的高性能分布式文件系统。 它的主要功能包括:文件存储,文件同步和文件访问,以及高容量和负载平衡。主要解决了海量数据存储问题,特别适合以中小文件(建议范围:1MB < file_size <500MB)为载体的在线服务。
// go run main.go --shardID 128 --id 1 --shardIDS "128,129" --ip 127.0.0.1 --port 9001 --peers "1=127.0.0.1:9001,2=127.0.0.1:9002,3=127.0.0.1:9003" --nodeInfo "1=127.0.0.1:9001,2=127.0.0.1:9002,3=127.0.0.1:9003,4=127.0.0.1:9004,5=127.0.0.1:9005,6=127.0.0.1:9006" --rpcPort 6061 --rpcPeers "1=127.0.0.1:6061,2=127.0.0.1:6062,3=127.0.0.1:6063" --rpcNodeInfo "1=127.0.0.1:6061,2=127.0.0.1:6062,3=127.0.0.1:6063,4=127.0.0.1:6064,5=127.0.0.1:6065,6=127.0.0.1:6066" --raftPort 19001 --raftPeers "1=127.0.0.1:19001,2=127.0.0.1:19002,3=127.0.0.1:19003" --raftNodeInfo "1=127.0.0.1:19001,2=127.0.0.1:19002,3=127.0.0.1:19003,4=127.0.0.1:19004,5=127.0.0.1:19005,6=127.0.0.1:19006" --logPort 8081 --logPeers "1=127.0.0.1:8081,2=127.0.0.1:8082,3=127.0.0.1:8083" --filePort 7071 --filePeers "1=127.0.0.1:7071,2=127.0.0.1:7072,3=127.0.0.1:7073" --incrementFilePort 5051 --incrementFilePeers "1=127.0.0.1:5051,2=127.0.0.1:5052,3=127.0.0.1:5053"
// go run main.go --shardID 128 --id 2 --shardIDS "128,129" --ip 127.0.0.1 --port 9002 --peers "1=127.0.0.1:9001,2=127.0.0.1:9002,3=127.0.0.1:9003" --nodeInfo "1=127.0.0.1:9001,2=127.0.0.1:9002,3=127.0.0.1:9003,4=127.0.0.1:9004,5=127.0.0.1:9005,6=127.0.0.1:9006" --rpcPort 6062 --rpcPeers "1=127.0.0.1:6061,2=127.0.0.1:6062,3=127.0.0.1:6063" --rpcNodeInfo "1=127.0.0.1:6061,2=127.0.0.1:6062,3=127.0.0.1:6063,4=127.0.0.1:6064,5=127.0.0.1:6065,6=127.0.0.1:6066" --raftPort 19002 --raftPeers "1=127.0.0.1:19001,2=127.0.0.1:19002,3=127.0.0.1:19003" --raftNodeInfo "1=127.0.0.1:19001,2=127.0.0.1:19002,3=127.0.0.1:19003,4=127.0.0.1:19004,5=127.0.0.1:19005,6=127.0.0.1:19006" --logPort 8082 --logPeers "1=127.0.0.1:8081,2=127.0.0.1:8082,3=127.0.0.1:8083" --filePort 7072 --filePeers "1=127.0.0.1:7071,2=127.0.0.1:7072,3=127.0.0.1:7073" --incrementFilePort 5052 --incrementFilePeers "1=127.0.0.1:5051,2=127.0.0.1:5052,3=127.0.0.1:5053"
// go run main.go --shardID 128 --id 3 --shardIDS "128,129" --ip 127.0.0.1 --port 9003 --peers "1=127.0.0.1:9001,2=127.0.0.1:9002,3=127.0.0.1:9003" --nodeInfo "1=127.0.0.1:9001,2=127.0.0.1:9002,3=127.0.0.1:9003,4=127.0.0.1:9004,5=127.0.0.1:9005,6=127.0.0.1:9006" --rpcPort 6063 --rpcPeers "1=127.0.0.1:6061,2=127.0.0.1:6062,3=127.0.0.1:6063" --rpcNodeInfo "1=127.0.0.1:6061,2=127.0.0.1:6062,3=127.0.0.1:6063,4=127.0.0.1:6064,5=127.0.0.1:6065,6=127.0.0.1:6066" --raftPort 19003 --raftPeers "1=127.0.0.1:19001,2=127.0.0.1:19002,3=127.0.0.1:19003" --raftNodeInfo "1=127.0.0.1:19001,2=127.0.0.1:19002,3=127.0.0.1:19003,4=127.0.0.1:19004,5=127.0.0.1:19005,6=127.0.0.1:19006" --logPort 8083 --logPeers "1=127.0.0.1:8081,2=127.0.0.1:8082,3=127.0.0.1:8083" --filePort 7073 --filePeers "1=127.0.0.1:7071,2=127.0.0.1:7072,3=127.0.0.1:7073" --incrementFilePort 5053 --incrementFilePeers "1=127.0.0.1:5051,2=127.0.0.1:5052,3=127.0.0.1:5053"
// go run main.go --shardID 129 --id 4 --shardIDS "128,129" --ip 127.0.0.1 --port 9004 --peers "4=127.0.0.1:9004,5=127.0.0.1:9005,6=127.0.0.1:9006" --nodeInfo "1=127.0.0.1:9001,2=127.0.0.1:9002,3=127.0.0.1:9003,4=127.0.0.1:9004,5=127.0.0.1:9005,6=127.0.0.1:9006" --rpcPort 6064 --rpcPeers "4=127.0.0.1:6064,5=127.0.0.1:6065,6=127.0.0.1:6066" --rpcNodeInfo "1=127.0.0.1:6061,2=127.0.0.1:6062,3=127.0.0.1:6063,4=127.0.0.1:6064,5=127.0.0.1:6065,6=127.0.0.1:6066" --raftPort 19004 --raftPeers "4=127.0.0.1:19004,5=127.0.0.1:19005,6=127.0.0.1:19006" --raftNodeInfo "1=127.0.0.1:19001,2=127.0.0.1:19002,3=127.0.0.1:19003,4=127.0.0.1:19004,5=127.0.0.1:19005,6=127.0.0.1:19006" --logPort 8084 --logPeers "4=127.0.0.1:8084,5=127.0.0.1:8085,6=127.0.0.1:8086" --filePort 7074 --filePeers "4=127.0.0.1:7074,5=127.0.0.1:7075,6=127.0.0.1:7076" --incrementFilePort 5054 --incrementFilePeers "4=127.0.0.1:5054,5=127.0.0.1:5055,6=127.0.0.1:5056"
// go run main.go --shardID 129 --id 5 --shardIDS "128,129" --ip 127.0.0.1 --port 9005 --peers "4=127.0.0.1:9004,5=127.0.0.1:9005,6=127.0.0.1:9006" --nodeInfo "1=127.0.0.1:9001,2=127.0.0.1:9002,3=127.0.0.1:9003,4=127.0.0.1:9004,5=127.0.0.1:9005,6=127.0.0.1:9006" --rpcPort 6065 --rpcPeers "4=127.0.0.1:6064,5=127.0.0.1:6065,6=127.0.0.1:6066" --rpcNodeInfo "1=127.0.0.1:6061,2=127.0.0.1:6062,3=127.0.0.1:6063,4=127.0.0.1:6064,5=127.0.0.1:6065,6=127.0.0.1:6066" --raftPort 19005 --raftPeers "4=127.0.0.1:19004,5=127.0.0.1:19005,6=127.0.0.1:19006" --raftNodeInfo "1=127.0.0.1:19001,2=127.0.0.1:19002,3=127.0.0.1:19003,4=127.0.0.1:19004,5=127.0.0.1:19005,6=127.0.0.1:19006" --logPort 8085 --logPeers "4=127.0.0.1:8084,5=127.0.0.1:8085,6=127.0.0.1:8086" --filePort 7075 --filePeers "4=127.0.0.1:7074,5=127.0.0.1:7075,6=127.0.0.1:7076" --incrementFilePort 5055 --incrementFilePeers "4=127.0.0.1:5054,5=127.0.0.1:5055,6=127.0.0.1:5056"
// go run main.go --shardID 129 --id 6 --shardIDS "128,129" --ip 127.0.0.1 --port 9006 --peers "4=127.0.0.1:9004,5=127.0.0.1:9005,6=127.0.0.1:9006" --nodeInfo "1=127.0.0.1:9001,2=127.0.0.1:9002,3=127.0.0.1:9003,4=127.0.0.1:9004,5=127.0.0.1:9005,6=127.0.0.1:9006" --rpcPort 6066 --rpcPeers "4=127.0.0.1:6064,5=127.0.0.1:6065,6=127.0.0.1:6066" --rpcNodeInfo "1=127.0.0.1:6061,2=127.0.0.1:6062,3=127.0.0.1:6063,4=127.0.0.1:6064,5=127.0.0.1:6065,6=127.0.0.1:6066" --raftPort 19006 --raftPeers "4=127.0.0.1:19004,5=127.0.0.1:19005,6=127.0.0.1:19006" --raftNodeInfo "1=127.0.0.1:19001,2=127.0.0.1:19002,3=127.0.0.1:19003,4=127.0.0.1:19004,5=127.0.0.1:19005,6=127.0.0.1:19006" --logPort 8086 --logPeers "4=127.0.0.1:8084,5=127.0.0.1:8085,6=127.0.0.1:8086" --filePort 7076 --filePeers "4=127.0.0.1:7074,5=127.0.0.1:7075,6=127.0.0.1:7076" --incrementFilePort 5056 --incrementFilePeers "4=127.0.0.1:5054,5=127.0.0.1:5055,6=127.0.0.1:5056"package main
import (
"fmt"
"os"
"time"
"mumu.com/redis-go/qfsSdk"
)
func main() {
// 1. 初始化SDK
qfsSdk.InitSDK(&qfsSdk.QFSConfig{
ClusterEntry: []string{
"127.0.0.1:9001",
"127.0.0.1:9002",
"127.0.0.1:9003",
},
Timeout: 15 * time.Second,
})
// 2. 读取测试文件
fileName := "22.pdf"
filePath := fmt.Sprintf("./%s", fileName)
file, err := os.Open(filePath)
if err != nil {
panic(err)
}
defer file.Close()
// 3. 直接调用SDK上传
resp, err := qfsSdk.Upload(file, fileName)
if err != nil {
panic(err)
}
println("上传结果:", string(resp))
}go mod init redis-cli
go build -o myredis-cli main.go
./myredis-cli 127.0.0.1:63791.使用HashiCorp Raft,测试通过主从切换 2.HTTP服务端口 :9001,9002 ,9003 3.RaftAddr:127.0.0.1:19001,127.0.0.1:19002,127.0.0.1:19003,
9.4. nh.StartOnDiskReplica(initialMembers, *join, dragonboatRaft.NewDiskKV, rc);默认initialMembers第一个启动的节点为主节点, [测试完成]
9.5. 1.dragonboat任何6个节点主从切换测试。[测试通过]2.官方设计就是:不同 ShardID 数据 100% 不互通、不同步![测试通过]3.数据要在每个分片单独写入4.定义全局shardID=9999,任何6个节点都能写入元数据和查询 [测试通过]
9.9. 1.启动6个节点,初始化槽信息,首次平均分配槽qfs_meta:9999:NodeSlotMetas[已完成] 2.存储每个shardID: qfs_meta:9999:128 qfs_meta:9999:129[已完成] 3.存储leaderID: get qfs_meta:9999:129:ShardNodeLeaderID get qfs_meta:9999:128:ShardNodeLeaderID,leader切换时,槽对应的leaderID要动态跟着修改 [已完成]
11.3.节点重启,从本地log日志读取最新日志自增编号,同时请求shard的leader获取最新的自增id,读取主节点的数据到获取的leader之前读取的最大自增id,接下来机器提供正常服务[已完成]
{"fileId":"145351743520108544","fileName":"22.pdf","filePath":"./file_data/data_7074/file_20260516/22.pdf","mineType":"application/pdf","fileSize":80529272,"createTime":1778880031113,"updateTime":1778880031113,"isDeleted":false,"status":"Pending"}
{"fileId":"145351743520108544","fileName":"22.pdf","filePath":"./file_data/data_7074/file_20260516/22.pdf","mineType":"application/pdf","fileSize":80529272,"createTime":1778880031113,"updateTime":1778880031113,"isDeleted":false,"status":"Pending"}
origin_1.log文件第一发任务后,offset=0读取文件为空,node_incre_task_manager文件DispatchOriginToWorker方法,batchReadLimitWithOffset
原始名称,新名称,创建时间,修改时间,图片大小,图片分辨率,MIME类型,图片格式jpg,图片内容的crc64校验图片完整性,
1.节点定时发心跳注册监控节点, 2.节点定时修改监控上注册的节点信息, 3.监控服务接口访问节点注册的数据,展示节点信息ip:port,权重,是否在线 4.使用hashmap存储节点信息
1.数据节点服务8080 2.监控节点服务9090 3.数据节点,监控节点分别存到hashmap 4.同时只有一个监控节点工作,其余节点待命1分钟
1.监控monitor节点通过发送json消息同步node节点数据 2.选举产生新的监控主节点(未实现)
1.raft测试通过github.com/hashicorp/raft 2.项目全部使用module形式发布
1.编码选主raft 2.编码发送主节点ip端口到数据节点
1.1.自动选主
1.2.元数据存储(1.master主节点槽数据信息 2.文件路径),落盘
2.1心跳定时检测各机子是否存活
2.2所有集群机器合集hasmap,master和slave对应关系,用masterNodeId=主机的id
2.2新机上线,添加到集群,hasmap添加,更新集群状态信息hasmap
2.3新机下线,从集群移除,hasmap删除,更新集群状态信息hasmap
3.1根据槽选择数据master主机,
3.2文件路径元数据存到主节点,自动同步到从节点
4.1jsonLog数据同步,master主机同步到slave从机(参照mysql的binlog)
命令扩容,参考Redis的命令
命令缩容,参考Redis的命令
7.1.集群状态监控(在线,下线,重启)
7.2.cpu和内存,磁盘使用率监控
7.3.面板动态扩容,面板动态缩容