用io.copy边读边切分大文件,配合io.sectionreader或io.limitreader控制内存;分片大小设4mb;每个分片单独计算sha256.sum256哈希并持久化;rpc需统一gob编码、导出方法与类型;http与rpc应分端口避免冲突。

怎么用 io.Copy 切分大文件而不爆内存
直接读整个文件进内存再切片是常见错误,尤其处理 GB 级文件时,runtime: out of memory 会立刻出现。关键不是“能不能切”,而是“边读边切”。io.Copy 配合 io.MultiWriter 或按偏移量构造 io.SectionReader 才能控制内存上限。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 用
os.Open打开源文件,不要os.ReadFile - 对每个分片创建独立的
*os.File,用io.Copy把对应段落写入(配合io.LimitReader或io.SectionReader) - 分片大小建议设为
4 * 1024 * 1024(4MB),太小增加 I/O 次数,太大仍可能触发 GC 压力 - 别在循环里反复
os.OpenFile(..., os.O_CREATE),先建好所有目标文件句柄再批量写,避免too many open files
如何用 sha256.Sum256 生成可比对的分片指纹
单纯按字节切分后,只要原始文件变一点,所有后续分片哈希全变——这会让去重和校验失效。必须对每个分片内容单独计算哈希,且保证分片边界固定(比如固定大小 + 最后一片补零或截断)。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 每个分片写完后立即用
sha256.Sum256计算哈希,存进元数据 map:map[string][32]byte - 别用
fmt.Sprintf("%x", hash)存,直接序列化[32]byte更省空间、无编码歧义 - 如果要支持断点续传,得把已写分片的哈希也持久化到本地临时文件,否则重启后无法判断哪些已成功
- 注意:
sha256.Sum256是值类型,sum := sha256.Sum256{}后必须调sum.Sum(nil)才能得到完整哈希字节
用 net/rpc 做节点间分片分发时,为什么总卡在 rpc: client protocol error
这不是网络不通,而是 net/rpc 默认只认 gob 编码,一旦服务端或客户端有一方用了 json 或没显式注册结构体,就会静默失败。更隐蔽的是,它不校验方法签名是否匹配,错一个字段名就报这个模糊错误。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 服务端注册前必须调
rpc.RegisterName("ShardService", &ShardServer{}),且ShardServer的方法必须是导出的(首字母大写),参数/返回值必须是导出类型 - 客户端连接后,立刻用
client.Call("ShardService.Upload", args, &reply)测试,别等业务逻辑堆完才验证 - 别在 RPC 方法里传
os.File或chan——gob不支持,会 panic - 调试时加一行
log.SetFlags(log.Lshortfile),并在 server 的 handler 开头打日志,确认是否进到了方法内部
本地测试多节点时,http.ListenAndServe 和 rpc.ServeConn 端口冲突怎么绕过
很多人想用 http 提供 UI,又用 rpc 做节点通信,结果两个都绑 :8080,后者直接报 listen tcp :8080: bind: address already in use。根本原因不是端口不够,而是没意识到 rpc.ServeConn 是面向连接的,不需要独占监听端口。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
-
rpc.ServeConn只用于已有连接(比如 HTTP upgrade 后的底层net.Conn),日常开发直接用rpc.ServeListener更稳 - 给 RPC 单独配端口,比如
:9090,HTTP 用:8080,两者互不干扰 - 如果真要复用 HTTP 端口(例如走 WebSocket tunnel),必须自己实现
http.Handler,在ServeHTTP里识别升级请求,再把conn交给rpc.ServeConn - 本地测试多个节点时,用
flag.Int控制端口,启动命令像:./server -port=8081 -rpc-port=9091
分片存储真正的复杂点不在切文件,而在“哪个分片该发给哪台机器”——一致性哈希、节点上下线、分片重平衡,这些逻辑一旦写死就很难改。原型阶段先用轮询或随机分发,但元数据结构里一定要预留 node_id 字段,别等后面加集群才发现 schema 绑死了。










