返回列表 发布新帖
查看: 51|回复: 0

使用 gRPC 双向流构建高性能文件传输服务

发表于 6 天前 | 查看全部 |阅读模式

这里或许是互联网从业者的最后一片净土,随客社区期待您的加入!

您需要 登录 才可以下载或查看,没有账号?立即注册

×
本帖最后由 mrkong 于 2025-4-25 15:53 编辑

背景与动机
在微服务架构下,服务之间传输大文件(如日志包、备份数据、模型文件等)是一项常见但挑战性较强的任务。传统的 HTTP 上传方式存在以下问题:
  • 大文件上传耗时长,阻塞线程
  • 不具备天然的断点续传能力
  • 传输过程中容易因网络抖动中断
为了解决这些问题,我们选择使用 gRPC 双向流(bidirectional streaming) 来实现更高效、更可控的文件传输机制。

为什么选择 gRPC Streaming?gRPC 是基于 HTTP/2 的高性能 RPC 框架,具备如下优势:
  • 双向流传输:支持客户端和服务端并发传输数据块
  • 内置连接管理、重试、压缩:提高稳定性
  • 二进制协议(protobuf):高效、易扩展
  • 天然适配微服务通信场景

系统设计概览基本流程
  • 客户端读取大文件,分块传输到服务端
  • 服务端接收块数据并按顺序写入本地临时文件
  • 支持进度回传、失败重试、文件完整性校验(SHA256)

protobuf 定义
  1. syntax = "proto3";

  2. service FileTransfer {
  3.   rpc Upload(stream FileChunk) returns (UploadStatus);
  4. }

  5. message FileChunk {
  6.   string filename = 1;
  7.   bytes content = 2;
  8.   int64 offset = 3;
  9.   bool eof = 4;
  10.   string sha256 = 5;
  11. }

  12. message UploadStatus {
  13.   bool success = 1;
  14.   string message = 2;
  15. }
复制代码
服务端实现(Go)
  1. func (s *Server) Upload(stream pb.FileTransfer_UploadServer) error {
  2.     var file *os.File
  3.     var filename string
  4.     var hasher = sha256.New()

  5.     for {
  6.         chunk, err := stream.Recv()
  7.         if err == io.EOF {
  8.             // 写入完成,校验 hash
  9.             calculated := hex.EncodeToString(hasher.Sum(nil))
  10.             if calculated != expectedHash {
  11.                 return stream.SendAndClose(&pb.UploadStatus{
  12.                     Success: false,
  13.                     Message: "SHA256 mismatch",
  14.                 })
  15.             }
  16.             return stream.SendAndClose(&pb.UploadStatus{
  17.                 Success: true,
  18.                 Message: "Upload complete",
  19.             })
  20.         }
  21.         if err != nil {
  22.             return err
  23.         }

  24.         if file == nil {
  25.             filename = chunk.Filename
  26.             file, err = os.Create("/tmp/" + filename)
  27.             if err != nil {
  28.                 return err
  29.             }
  30.             defer file.Close()
  31.         }

  32.         file.WriteAt(chunk.Content, chunk.Offset)
  33.         hasher.Write(chunk.Content)
  34.     }
  35. }
复制代码
客户端实现(Go)
  1. stream, err := client.Upload(context.Background())
  2. if err != nil {
  3.     log.Fatal(err)
  4. }

  5. buf := make([]byte, 1024*1024) // 1MB block
  6. var offset int64
  7. file, _ := os.Open("large_file.zip")
  8. defer file.Close()

  9. h := sha256.New()

  10. for {
  11.     n, err := file.Read(buf)
  12.     if err == io.EOF {
  13.         break
  14.     }
  15.     h.Write(buf[:n])

  16.     chunk := &pb.FileChunk{
  17.         Filename: "large_file.zip",
  18.         Content:  buf[:n],
  19.         Offset:   offset,
  20.         Sha256:   "", // 可在最后一块填入完整值
  21.     }
  22.     offset += int64(n)
  23.     stream.Send(chunk)
  24. }

  25. // 最后一块带 eof=true 和 sha256 值
  26. stream.Send(&pb.FileChunk{
  27.     Filename: "large_file.zip",
  28.     Content:  nil,
  29.     Offset:   offset,
  30.     Sha256:   hex.EncodeToString(h.Sum(nil)),
  31.     Eof:      true,
  32. })

  33. res, err := stream.CloseAndRecv()
  34. log.Println("Upload result:", res.Message)
复制代码
性能与可靠性优化建议
方向建议
并发传输多文件同时传输可开多连接并发
压缩算法传输前压缩(如 gzip)降低带宽占用
中断恢复记录 offset,失败后从断点续传
限流保护gRPC 配置并发数和带宽限速

总结
使用 gRPC 双向流传输大文件具有高效、稳定、可控的优点。相比传统 HTTP 上传,它适合微服务体系内的大规模数据同步、备份、模型分发等场景。未来也可以进一步结合传输调度、服务发现机制,实现跨集群传输任务自动调度。
如果你在项目中有大文件传输需求,gRPC streaming 是值得尝试的解决方案之一。

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Copyright © 2001-2025 Suike Tech All Rights Reserved. 随客交流社区 (备案号:津ICP备19010126号) |Processed in 0.108943 second(s), 9 queries , Gzip On, MemCached On.
关灯 在本版发帖返回顶部
快速回复 返回顶部 返回列表