这里或许是互联网从业者的最后一片净土,随客社区期待您的加入!
您需要 登录 才可以下载或查看,没有账号?立即注册
×
本帖最后由 mrkong 于 2025-4-25 15:53 编辑
背景与动机
在微服务架构下,服务之间传输大文件(如日志包、备份数据、模型文件等)是一项常见但挑战性较强的任务。传统的 HTTP 上传方式存在以下问题: - 大文件上传耗时长,阻塞线程
- 不具备天然的断点续传能力
- 传输过程中容易因网络抖动中断
为了解决这些问题,我们选择使用 gRPC 双向流(bidirectional streaming) 来实现更高效、更可控的文件传输机制。
为什么选择 gRPC Streaming?gRPC 是基于 HTTP/2 的高性能 RPC 框架,具备如下优势:
- 双向流传输:支持客户端和服务端并发传输数据块
- 内置连接管理、重试、压缩:提高稳定性
- 二进制协议(protobuf):高效、易扩展
- 天然适配微服务通信场景
系统设计概览基本流程- 客户端读取大文件,分块传输到服务端
- 服务端接收块数据并按顺序写入本地临时文件
- 支持进度回传、失败重试、文件完整性校验(SHA256)
protobuf 定义
- syntax = "proto3";
- service FileTransfer {
- rpc Upload(stream FileChunk) returns (UploadStatus);
- }
- message FileChunk {
- string filename = 1;
- bytes content = 2;
- int64 offset = 3;
- bool eof = 4;
- string sha256 = 5;
- }
- message UploadStatus {
- bool success = 1;
- string message = 2;
- }
复制代码 服务端实现(Go)
- func (s *Server) Upload(stream pb.FileTransfer_UploadServer) error {
- var file *os.File
- var filename string
- var hasher = sha256.New()
- for {
- chunk, err := stream.Recv()
- if err == io.EOF {
- // 写入完成,校验 hash
- calculated := hex.EncodeToString(hasher.Sum(nil))
- if calculated != expectedHash {
- return stream.SendAndClose(&pb.UploadStatus{
- Success: false,
- Message: "SHA256 mismatch",
- })
- }
- return stream.SendAndClose(&pb.UploadStatus{
- Success: true,
- Message: "Upload complete",
- })
- }
- if err != nil {
- return err
- }
- if file == nil {
- filename = chunk.Filename
- file, err = os.Create("/tmp/" + filename)
- if err != nil {
- return err
- }
- defer file.Close()
- }
- file.WriteAt(chunk.Content, chunk.Offset)
- hasher.Write(chunk.Content)
- }
- }
复制代码 客户端实现(Go)
- stream, err := client.Upload(context.Background())
- if err != nil {
- log.Fatal(err)
- }
- buf := make([]byte, 1024*1024) // 1MB block
- var offset int64
- file, _ := os.Open("large_file.zip")
- defer file.Close()
- h := sha256.New()
- for {
- n, err := file.Read(buf)
- if err == io.EOF {
- break
- }
- h.Write(buf[:n])
- chunk := &pb.FileChunk{
- Filename: "large_file.zip",
- Content: buf[:n],
- Offset: offset,
- Sha256: "", // 可在最后一块填入完整值
- }
- offset += int64(n)
- stream.Send(chunk)
- }
- // 最后一块带 eof=true 和 sha256 值
- stream.Send(&pb.FileChunk{
- Filename: "large_file.zip",
- Content: nil,
- Offset: offset,
- Sha256: hex.EncodeToString(h.Sum(nil)),
- Eof: true,
- })
- res, err := stream.CloseAndRecv()
- log.Println("Upload result:", res.Message)
复制代码 性能与可靠性优化建议
方向 | 建议 | 并发传输 | 多文件同时传输可开多连接并发 | 压缩算法 | 传输前压缩(如 gzip)降低带宽占用 | 中断恢复 | 记录 offset,失败后从断点续传 | 限流保护 | gRPC 配置并发数和带宽限速 |
总结
使用 gRPC 双向流传输大文件具有高效、稳定、可控的优点。相比传统 HTTP 上传,它适合微服务体系内的大规模数据同步、备份、模型分发等场景。未来也可以进一步结合传输调度、服务发现机制,实现跨集群传输任务自动调度。 如果你在项目中有大文件传输需求,gRPC streaming 是值得尝试的解决方案之一。
|