You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

io.go 6.6 kB

2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. package grpc
  2. import (
  3. "context"
  4. "fmt"
  5. "io"
  6. "time"
  7. "gitlink.org.cn/cloudream/common/pkgs/logger"
  8. "gitlink.org.cn/cloudream/common/utils/io2"
  9. "gitlink.org.cn/cloudream/common/utils/serder"
  10. agtrpc "gitlink.org.cn/cloudream/storage/common/pkgs/grpc/agent"
  11. "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch"
  12. )
  13. func (s *Service) ExecuteIOPlan(ctx context.Context, req *agtrpc.ExecuteIOPlanReq) (*agtrpc.ExecuteIOPlanResp, error) {
  14. plan, err := serder.JSONToObjectEx[ioswitch.Plan]([]byte(req.Plan))
  15. if err != nil {
  16. return nil, fmt.Errorf("deserializing plan: %w", err)
  17. }
  18. logger.WithField("PlanID", plan.ID).Infof("begin execute io plan")
  19. defer logger.WithField("PlanID", plan.ID).Infof("plan finished")
  20. sw := ioswitch.NewSwitch(plan)
  21. s.swMgr.Add(sw)
  22. defer s.swMgr.Remove(sw)
  23. err = sw.Run(ctx)
  24. if err != nil {
  25. return nil, fmt.Errorf("running io plan: %w", err)
  26. }
  27. return &agtrpc.ExecuteIOPlanResp{}, nil
  28. }
  29. func (s *Service) SendStream(server agtrpc.Agent_SendStreamServer) error {
  30. msg, err := server.Recv()
  31. if err != nil {
  32. return fmt.Errorf("recving stream id packet: %w", err)
  33. }
  34. if msg.Type != agtrpc.StreamDataPacketType_SendArgs {
  35. return fmt.Errorf("first packet must be a SendArgs packet")
  36. }
  37. logger.
  38. WithField("PlanID", msg.PlanID).
  39. WithField("VarID", msg.VarID).
  40. Debugf("receive stream")
  41. // 同一批Plan中每个节点的Plan的启动时间有先后,但最多不应该超过30秒
  42. ctx, cancel := context.WithTimeout(server.Context(), time.Second*30)
  43. defer cancel()
  44. sw := s.swMgr.FindByIDContexted(ctx, ioswitch.PlanID(msg.PlanID))
  45. if sw == nil {
  46. return fmt.Errorf("plan not found")
  47. }
  48. pr, pw := io.Pipe()
  49. varID := ioswitch.VarID(msg.VarID)
  50. sw.PutVars(&ioswitch.StreamVar{
  51. ID: varID,
  52. Stream: pr,
  53. })
  54. // 然后读取文件数据
  55. var recvSize int64
  56. for {
  57. msg, err := server.Recv()
  58. // 读取客户端数据失败
  59. // 即使err是io.EOF,只要没有收到客户端包含EOF数据包就被断开了连接,就认为接收失败
  60. if err != nil {
  61. // 关闭文件写入
  62. pw.CloseWithError(io.ErrClosedPipe)
  63. logger.WithField("ReceiveSize", recvSize).
  64. WithField("VarID", varID).
  65. Warnf("recv message failed, err: %s", err.Error())
  66. return fmt.Errorf("recv message failed, err: %w", err)
  67. }
  68. err = io2.WriteAll(pw, msg.Data)
  69. if err != nil {
  70. // 关闭文件写入
  71. pw.CloseWithError(io.ErrClosedPipe)
  72. logger.Warnf("write data to file failed, err: %s", err.Error())
  73. return fmt.Errorf("write data to file failed, err: %w", err)
  74. }
  75. recvSize += int64(len(msg.Data))
  76. if msg.Type == agtrpc.StreamDataPacketType_EOF {
  77. // 客户端明确说明文件传输已经结束,那么结束写入,获得文件Hash
  78. err := pw.Close()
  79. if err != nil {
  80. logger.Warnf("finish writing failed, err: %s", err.Error())
  81. return fmt.Errorf("finish writing failed, err: %w", err)
  82. }
  83. // 并将结果返回到客户端
  84. err = server.SendAndClose(&agtrpc.SendStreamResp{})
  85. if err != nil {
  86. logger.Warnf("send response failed, err: %s", err.Error())
  87. return fmt.Errorf("send response failed, err: %w", err)
  88. }
  89. return nil
  90. }
  91. }
  92. }
  93. func (s *Service) GetStream(req *agtrpc.GetStreamReq, server agtrpc.Agent_GetStreamServer) error {
  94. logger.
  95. WithField("PlanID", req.PlanID).
  96. WithField("VarID", req.VarID).
  97. Debugf("send stream")
  98. // 同上
  99. ctx, cancel := context.WithTimeout(server.Context(), time.Second*30)
  100. defer cancel()
  101. sw := s.swMgr.FindByIDContexted(ctx, ioswitch.PlanID(req.PlanID))
  102. if sw == nil {
  103. return fmt.Errorf("plan not found")
  104. }
  105. signal, err := serder.JSONToObjectEx[*ioswitch.SignalVar]([]byte(req.Signal))
  106. if err != nil {
  107. return fmt.Errorf("deserializing var: %w", err)
  108. }
  109. sw.PutVars(signal)
  110. strVar := &ioswitch.StreamVar{
  111. ID: ioswitch.VarID(req.VarID),
  112. }
  113. err = sw.BindVars(server.Context(), strVar)
  114. if err != nil {
  115. return fmt.Errorf("binding vars: %w", err)
  116. }
  117. reader := strVar.Stream
  118. defer reader.Close()
  119. buf := make([]byte, 1024*64)
  120. readAllCnt := 0
  121. for {
  122. readCnt, err := reader.Read(buf)
  123. if readCnt > 0 {
  124. readAllCnt += readCnt
  125. err = server.Send(&agtrpc.StreamDataPacket{
  126. Type: agtrpc.StreamDataPacketType_Data,
  127. Data: buf[:readCnt],
  128. })
  129. if err != nil {
  130. logger.
  131. WithField("PlanID", req.PlanID).
  132. WithField("VarID", req.VarID).
  133. Warnf("send stream data failed, err: %s", err.Error())
  134. return fmt.Errorf("send stream data failed, err: %w", err)
  135. }
  136. }
  137. // 文件读取完毕
  138. if err == io.EOF {
  139. logger.
  140. WithField("PlanID", req.PlanID).
  141. WithField("VarID", req.VarID).
  142. Debugf("send data size %d", readAllCnt)
  143. // 发送EOF消息
  144. server.Send(&agtrpc.StreamDataPacket{
  145. Type: agtrpc.StreamDataPacketType_EOF,
  146. })
  147. return nil
  148. }
  149. // io.ErrUnexpectedEOF没有读满整个buf就遇到了EOF,此时正常发送剩余数据即可。除了这两个错误之外,其他错误都中断操作
  150. if err != nil && err != io.ErrUnexpectedEOF {
  151. logger.
  152. WithField("PlanID", req.PlanID).
  153. WithField("VarID", req.VarID).
  154. Warnf("reading stream data: %s", err.Error())
  155. return fmt.Errorf("reading stream data: %w", err)
  156. }
  157. }
  158. }
  159. func (s *Service) SendVar(ctx context.Context, req *agtrpc.SendVarReq) (*agtrpc.SendVarResp, error) {
  160. ctx, cancel := context.WithTimeout(ctx, time.Second*30)
  161. defer cancel()
  162. sw := s.swMgr.FindByIDContexted(ctx, ioswitch.PlanID(req.PlanID))
  163. if sw == nil {
  164. return nil, fmt.Errorf("plan not found")
  165. }
  166. v, err := serder.JSONToObjectEx[ioswitch.Var]([]byte(req.Var))
  167. if err != nil {
  168. return nil, fmt.Errorf("deserializing var: %w", err)
  169. }
  170. sw.PutVars(v)
  171. return &agtrpc.SendVarResp{}, nil
  172. }
  173. func (s *Service) GetVar(ctx context.Context, req *agtrpc.GetVarReq) (*agtrpc.GetVarResp, error) {
  174. ctx2, cancel := context.WithTimeout(ctx, time.Second*30)
  175. defer cancel()
  176. sw := s.swMgr.FindByIDContexted(ctx2, ioswitch.PlanID(req.PlanID))
  177. if sw == nil {
  178. return nil, fmt.Errorf("plan not found")
  179. }
  180. v, err := serder.JSONToObjectEx[ioswitch.Var]([]byte(req.Var))
  181. if err != nil {
  182. return nil, fmt.Errorf("deserializing var: %w", err)
  183. }
  184. signal, err := serder.JSONToObjectEx[*ioswitch.SignalVar]([]byte(req.Signal))
  185. if err != nil {
  186. return nil, fmt.Errorf("deserializing var: %w", err)
  187. }
  188. sw.PutVars(signal)
  189. err = sw.BindVars(ctx, v)
  190. if err != nil {
  191. return nil, fmt.Errorf("binding vars: %w", err)
  192. }
  193. vd, err := serder.ObjectToJSONEx(v)
  194. if err != nil {
  195. return nil, fmt.Errorf("serializing var: %w", err)
  196. }
  197. return &agtrpc.GetVarResp{
  198. Var: string(vd),
  199. }, nil
  200. }

本项目旨在将云际存储公共基础设施化,使个人及企业可低门槛使用高效的云际存储服务(安装开箱即用云际存储客户端即可,无需关注其他组件的部署),同时支持用户灵活便捷定制云际存储的功能细节。