## 背景与适用场景
大型文件存储场景下,单机容量和可靠性都存在瓶颈。分布式文件系统通过横向扩展节点来解决,但许多业务场景不需要完整的HDFS或Ceph,一个轻量级、可验证的原型更有助于理解核心机制。本文基于Python 3 + FastAPI + SQLite实现了一个全栈分布式文件系统原型,涵盖了用户认证、节点管理、文件分片与多副本冗余、读取时完整性校验等完整业务闭环。代码可直接运行于本地环境,适合作为全栈课程或后端工程实践的基础版本。
## 技术栈与依赖
- Python 3 + FastAPI:异步Web框架,用于REST API
- SQLAlchemy + SQLite:ORM与轻量级数据库,存储元数据与分片内容(生产可替换为对象存储)
- Pydantic:数据验证
- Passlib bcrypt:密码哈希与验证
- Vue 3 + Vite:前端管理页面(本文重点分析后端代码逻辑)
- uvicorn:ASGI服务器
## 核心架构与数据模型
系统分为三层:Vue前端 → FastAPI后端 → SQLite数据库。四个核心模型:
- users:用户ID、用户名、bcrypt哈希密码、注册时间
- storage_nodes:节点ID、名称、地址、容量、已用容量、在线状态
- stored_files:文件ID、文件名、文件大小、完整文件SHA-256校验和、期望副本数、所属用户ID
- file_chunks:分片ID、所属文件ID、所在节点ID、分片序号、分片内容、分片SHA-256校验和
文件上传时,后端将内容按固定大小切片,并根据副本数将每个分片分配到不同在线节点;读取时按序号选择任一可用副本重组,并与文件级checksum比对验证数据完整性。
## 关键代码实现解析
### 1. 数据库连接与会话管理
以下代码位于 backend/app/database.py,创建SQLite引擎并配置FastAPI依赖注入:- SQLALCHEMY_DATABASE_URL = "sqlite:///./dfs.db"
- engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False})
- SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
- def get_db():
- db = SessionLocal()
- try:
- yield db
- finally:
- db.close()
复制代码 注意加上了check_same_thread=False以允许跨线程复用同一连接,这是SQLite在FastAPI中的常见配置。
### 2. 密码哈希与Token签发
使用Passlib的bcrypt算法避免明文存储密码。Token采用自定义格式:用户ID + 过期时间戳 + 随机nonce + SHA-256签名,签名密钥存储在服务端。- from passlib.context import CryptContext
- pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
- def hash_password(password: str) -> str:
- return pwd_context.hash(password)
- def verify_password(password: str, password_hash: str) -> bool:
- return pwd_context.verify(password, password_hash)
复制代码 登录成功后,生成token并返回客户端,所有受保护接口依赖get_current_user函数解析并校验token有效性。
### 3. 文件切片与多副本分配
上传时先查询在线节点列表,然后将文件内容按CHUNK_SIZE(示例中为64字符)分割为多个chunk。每个chunk根据副本数写入不同节点,通过取模算法实现副本分布在不同节点上,提高可用性。- CHUNK_SIZE = 64
- chunks = [content[i : i + CHUNK_SIZE] for i in range(0, len(content), CHUNK_SIZE)]
- for idx, chunk in enumerate(chunks):
- for replica in range(real_replica_count):
- node = online_nodes[(idx + replica) % len(online_nodes)]
- db.add(FileChunk(
- file_id=file.id,
- node_id=node.id,
- chunk_index=idx,
- data=chunk,
- checksum=sha256_text(chunk)
- ))
复制代码 关键设计:每个分片也单独计算checksum,便于后续验证单个分片是否损坏。副本分配策略采用简单的轮询+偏移量,保证同一分片的多个副本不会落在同一节点上(节点数足够时)。
### 4. 文件读取与完整性校验
读取时,系统获取所有分片记录,对于每个chunk_index选择第一个可用的副本(按node_id排序或随机),按序号拼接完整内容,最后与文件级checksum比对。- first_replica_by_index = {}
- for chunk in file_chunks:
- if chunk.chunk_index not in first_replica_by_index:
- first_replica_by_index[chunk.chunk_index] = chunk.data
- content = "".join(first_replica_by_index[index] for index in sorted(first_replica_by_index))
- if sha256_text(content) != file.checksum:
- raise HTTPException(status_code=500, detail="文件校验失败,数据可能不完整")
复制代码 当某个节点下线导致部分分片缺失时,如果其他副本存在,仍然能读取成功;但若某个索引的所有副本都不可用,则会触发校验失败。这种设计能够快速检测数据损坏。
### 5. 前端鉴权请求封装
前端api.js中统一处理token注入。从localStorage读取token并附加到Authorization头,退出登录时清除token。- const token = getToken()
- if (token) headers.Authorization = `Bearer ${token}`
复制代码 后端通过FastAPI的Depends(get_current_user)自动校验所有受保护接口。
## 部署与测试流程
1. 启动后端:进入backend目录,创建虚拟环境并安装依赖,运行uvicorn app.main:app --reload --host 0.0.0.0 --port 8000。Swagger文档可通过http://127.0.0.1:8000/docs访问。
2. 启动前端:进入frontend目录,执行npm install后npm run dev,默认访问http://127.0.0.1:5173。若后端地址不同,通过VITE_API_BASE环境变量配置。
3. 使用流程:注册用户→登录→查看默认三个节点→上传一个文本文件(可设置副本数)→查看文件列表中的分片副本数量→尝试下线一个节点后继续上传,观察副本分布变化→点击读取文件验证重组与校验逻辑。
## 可扩展方向
本项目作为最小可行原型,后续可扩展真实文件上传(而非直接传文本)、断点续传、节点健康检查、后台副本修复、用户空间配额、集成对象存储(如MinIO)以及添加Prometheus监控指标。所有核心代码均基于Python标准库和流行的FastAPI生态,结构清晰,适合作为分布式存储学习或面试项目的基础框架。 |