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.7 kB

2 years ago
2 years ago
2 years ago
1 year ago
2 years ago
2 years ago
2 years ago
2 years ago
1 year ago
2 years ago
1 year 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
1 year ago
2 years ago
1 year ago
1 year ago
1 year ago
2 years ago
2 years ago
2 years ago
2 years ago
1 year ago
2 years ago
2 years ago
2 years ago
1 year ago
2 years ago
1 year ago
2 years ago
2 years ago
2 years ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. package grpc
  2. import (
  3. "context"
  4. "fmt"
  5. "io"
  6. "time"
  7. "github.com/inhies/go-bytesize"
  8. "gitlink.org.cn/cloudream/common/pkgs/ioswitch/exec"
  9. "gitlink.org.cn/cloudream/common/pkgs/logger"
  10. "gitlink.org.cn/cloudream/common/utils/io2"
  11. "gitlink.org.cn/cloudream/common/utils/serder"
  12. agtrpc "gitlink.org.cn/cloudream/storage/common/pkgs/grpc/agent"
  13. )
  14. func (s *Service) ExecuteIOPlan(ctx context.Context, req *agtrpc.ExecuteIOPlanReq) (*agtrpc.ExecuteIOPlanResp, error) {
  15. plan, err := serder.JSONToObjectEx[exec.Plan]([]byte(req.Plan))
  16. if err != nil {
  17. return nil, fmt.Errorf("deserializing plan: %w", err)
  18. }
  19. logger.WithField("PlanID", plan.ID).Infof("begin execute io plan")
  20. defer logger.WithField("PlanID", plan.ID).Infof("plan finished")
  21. sw := exec.NewExecutor(plan)
  22. s.swWorker.Add(sw)
  23. defer s.swWorker.Remove(sw)
  24. execCtx := exec.NewWithContext(ctx)
  25. // TODO2 注入依赖
  26. _, err = sw.Run(execCtx)
  27. if err != nil {
  28. return nil, fmt.Errorf("running io plan: %w", err)
  29. }
  30. return &agtrpc.ExecuteIOPlanResp{}, nil
  31. }
  32. func (s *Service) SendStream(server agtrpc.Agent_SendStreamServer) error {
  33. msg, err := server.Recv()
  34. if err != nil {
  35. return fmt.Errorf("recving stream id packet: %w", err)
  36. }
  37. if msg.Type != agtrpc.StreamDataPacketType_SendArgs {
  38. return fmt.Errorf("first packet must be a SendArgs packet")
  39. }
  40. logger.
  41. WithField("PlanID", msg.PlanID).
  42. WithField("VarID", msg.VarID).
  43. Debugf("stream input")
  44. // 同一批Plan中每个节点的Plan的启动时间有先后,但最多不应该超过30秒
  45. ctx, cancel := context.WithTimeout(server.Context(), time.Second*30)
  46. defer cancel()
  47. sw := s.swWorker.FindByIDContexted(ctx, exec.PlanID(msg.PlanID))
  48. if sw == nil {
  49. return fmt.Errorf("plan not found")
  50. }
  51. pr, pw := io.Pipe()
  52. varID := exec.VarID(msg.VarID)
  53. sw.PutVar(varID, &exec.StreamValue{Stream: pr})
  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("stream output")
  98. // 同上
  99. ctx, cancel := context.WithTimeout(server.Context(), time.Second*30)
  100. defer cancel()
  101. sw := s.swWorker.FindByIDContexted(ctx, exec.PlanID(req.PlanID))
  102. if sw == nil {
  103. return fmt.Errorf("plan not found")
  104. }
  105. signal, err := serder.JSONToObjectEx[exec.VarValue]([]byte(req.Signal))
  106. if err != nil {
  107. return fmt.Errorf("deserializing var: %w", err)
  108. }
  109. sw.PutVar(exec.VarID(req.SignalID), signal)
  110. strVar, err := exec.BindVar[*exec.StreamValue](sw, server.Context(), exec.VarID(req.VarID))
  111. if err != nil {
  112. return fmt.Errorf("binding vars: %w", err)
  113. }
  114. reader := strVar.Stream
  115. defer reader.Close()
  116. buf := make([]byte, 1024*64)
  117. readAllCnt := 0
  118. startTime := time.Now()
  119. for {
  120. readCnt, err := reader.Read(buf)
  121. if readCnt > 0 {
  122. readAllCnt += readCnt
  123. err = server.Send(&agtrpc.StreamDataPacket{
  124. Type: agtrpc.StreamDataPacketType_Data,
  125. Data: buf[:readCnt],
  126. })
  127. if err != nil {
  128. logger.
  129. WithField("PlanID", req.PlanID).
  130. WithField("VarID", req.VarID).
  131. Warnf("send stream data failed, err: %s", err.Error())
  132. return fmt.Errorf("send stream data failed, err: %w", err)
  133. }
  134. }
  135. // 文件读取完毕
  136. if err == io.EOF {
  137. dt := time.Since(startTime)
  138. logger.
  139. WithField("PlanID", req.PlanID).
  140. WithField("VarID", req.VarID).
  141. Debugf("send data size %d in %v, speed %v/s", readAllCnt, dt, bytesize.New(float64(readAllCnt)/dt.Seconds()))
  142. // 发送EOF消息
  143. server.Send(&agtrpc.StreamDataPacket{
  144. Type: agtrpc.StreamDataPacketType_EOF,
  145. })
  146. return nil
  147. }
  148. // io.ErrUnexpectedEOF没有读满整个buf就遇到了EOF,此时正常发送剩余数据即可。除了这两个错误之外,其他错误都中断操作
  149. if err != nil && err != io.ErrUnexpectedEOF {
  150. logger.
  151. WithField("PlanID", req.PlanID).
  152. WithField("VarID", req.VarID).
  153. Warnf("reading stream data: %s", err.Error())
  154. return fmt.Errorf("reading stream data: %w", err)
  155. }
  156. }
  157. }
  158. func (s *Service) SendVar(ctx context.Context, req *agtrpc.SendVarReq) (*agtrpc.SendVarResp, error) {
  159. ctx, cancel := context.WithTimeout(ctx, time.Second*30)
  160. defer cancel()
  161. sw := s.swWorker.FindByIDContexted(ctx, exec.PlanID(req.PlanID))
  162. if sw == nil {
  163. return nil, fmt.Errorf("plan not found")
  164. }
  165. v, err := serder.JSONToObjectEx[exec.VarValue]([]byte(req.VarValue))
  166. if err != nil {
  167. return nil, fmt.Errorf("deserializing var: %w", err)
  168. }
  169. sw.PutVar(exec.VarID(req.VarID), v)
  170. return &agtrpc.SendVarResp{}, nil
  171. }
  172. func (s *Service) GetVar(ctx context.Context, req *agtrpc.GetVarReq) (*agtrpc.GetVarResp, error) {
  173. ctx2, cancel := context.WithTimeout(ctx, time.Second*30)
  174. defer cancel()
  175. sw := s.swWorker.FindByIDContexted(ctx2, exec.PlanID(req.PlanID))
  176. if sw == nil {
  177. return nil, fmt.Errorf("plan not found")
  178. }
  179. signal, err := serder.JSONToObjectEx[exec.VarValue]([]byte(req.Signal))
  180. if err != nil {
  181. return nil, fmt.Errorf("deserializing var: %w", err)
  182. }
  183. sw.PutVar(exec.VarID(req.SignalID), signal)
  184. v, err := sw.BindVar(ctx, exec.VarID(req.VarID))
  185. if err != nil {
  186. return nil, fmt.Errorf("binding vars: %w", err)
  187. }
  188. vd, err := serder.ObjectToJSONEx(v)
  189. if err != nil {
  190. return nil, fmt.Errorf("serializing var: %w", err)
  191. }
  192. return &agtrpc.GetVarResp{
  193. Var: string(vd),
  194. }, nil
  195. }

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