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.8 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
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
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
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  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.PutVars(&exec.StreamVar{
  54. ID: varID,
  55. Stream: pr,
  56. })
  57. // 然后读取文件数据
  58. var recvSize int64
  59. for {
  60. msg, err := server.Recv()
  61. // 读取客户端数据失败
  62. // 即使err是io.EOF,只要没有收到客户端包含EOF数据包就被断开了连接,就认为接收失败
  63. if err != nil {
  64. // 关闭文件写入
  65. pw.CloseWithError(io.ErrClosedPipe)
  66. logger.WithField("ReceiveSize", recvSize).
  67. WithField("VarID", varID).
  68. Warnf("recv message failed, err: %s", err.Error())
  69. return fmt.Errorf("recv message failed, err: %w", err)
  70. }
  71. err = io2.WriteAll(pw, msg.Data)
  72. if err != nil {
  73. // 关闭文件写入
  74. pw.CloseWithError(io.ErrClosedPipe)
  75. logger.Warnf("write data to file failed, err: %s", err.Error())
  76. return fmt.Errorf("write data to file failed, err: %w", err)
  77. }
  78. recvSize += int64(len(msg.Data))
  79. if msg.Type == agtrpc.StreamDataPacketType_EOF {
  80. // 客户端明确说明文件传输已经结束,那么结束写入,获得文件Hash
  81. err := pw.Close()
  82. if err != nil {
  83. logger.Warnf("finish writing failed, err: %s", err.Error())
  84. return fmt.Errorf("finish writing failed, err: %w", err)
  85. }
  86. // 并将结果返回到客户端
  87. err = server.SendAndClose(&agtrpc.SendStreamResp{})
  88. if err != nil {
  89. logger.Warnf("send response failed, err: %s", err.Error())
  90. return fmt.Errorf("send response failed, err: %w", err)
  91. }
  92. return nil
  93. }
  94. }
  95. }
  96. func (s *Service) GetStream(req *agtrpc.GetStreamReq, server agtrpc.Agent_GetStreamServer) error {
  97. logger.
  98. WithField("PlanID", req.PlanID).
  99. WithField("VarID", req.VarID).
  100. Debugf("stream output")
  101. // 同上
  102. ctx, cancel := context.WithTimeout(server.Context(), time.Second*30)
  103. defer cancel()
  104. sw := s.swWorker.FindByIDContexted(ctx, exec.PlanID(req.PlanID))
  105. if sw == nil {
  106. return fmt.Errorf("plan not found")
  107. }
  108. signal, err := serder.JSONToObjectEx[*exec.SignalVar]([]byte(req.Signal))
  109. if err != nil {
  110. return fmt.Errorf("deserializing var: %w", err)
  111. }
  112. sw.PutVars(signal)
  113. strVar := &exec.StreamVar{
  114. ID: exec.VarID(req.VarID),
  115. }
  116. err = sw.BindVars(server.Context(), strVar)
  117. if err != nil {
  118. return fmt.Errorf("binding vars: %w", err)
  119. }
  120. reader := strVar.Stream
  121. defer reader.Close()
  122. buf := make([]byte, 1024*64)
  123. readAllCnt := 0
  124. startTime := time.Now()
  125. for {
  126. readCnt, err := reader.Read(buf)
  127. if readCnt > 0 {
  128. readAllCnt += readCnt
  129. err = server.Send(&agtrpc.StreamDataPacket{
  130. Type: agtrpc.StreamDataPacketType_Data,
  131. Data: buf[:readCnt],
  132. })
  133. if err != nil {
  134. logger.
  135. WithField("PlanID", req.PlanID).
  136. WithField("VarID", req.VarID).
  137. Warnf("send stream data failed, err: %s", err.Error())
  138. return fmt.Errorf("send stream data failed, err: %w", err)
  139. }
  140. }
  141. // 文件读取完毕
  142. if err == io.EOF {
  143. dt := time.Since(startTime)
  144. logger.
  145. WithField("PlanID", req.PlanID).
  146. WithField("VarID", req.VarID).
  147. Debugf("send data size %d in %v, speed %v/s", readAllCnt, dt, bytesize.New(float64(readAllCnt)/dt.Seconds()))
  148. // 发送EOF消息
  149. server.Send(&agtrpc.StreamDataPacket{
  150. Type: agtrpc.StreamDataPacketType_EOF,
  151. })
  152. return nil
  153. }
  154. // io.ErrUnexpectedEOF没有读满整个buf就遇到了EOF,此时正常发送剩余数据即可。除了这两个错误之外,其他错误都中断操作
  155. if err != nil && err != io.ErrUnexpectedEOF {
  156. logger.
  157. WithField("PlanID", req.PlanID).
  158. WithField("VarID", req.VarID).
  159. Warnf("reading stream data: %s", err.Error())
  160. return fmt.Errorf("reading stream data: %w", err)
  161. }
  162. }
  163. }
  164. func (s *Service) SendVar(ctx context.Context, req *agtrpc.SendVarReq) (*agtrpc.SendVarResp, error) {
  165. ctx, cancel := context.WithTimeout(ctx, time.Second*30)
  166. defer cancel()
  167. sw := s.swWorker.FindByIDContexted(ctx, exec.PlanID(req.PlanID))
  168. if sw == nil {
  169. return nil, fmt.Errorf("plan not found")
  170. }
  171. v, err := serder.JSONToObjectEx[exec.Var]([]byte(req.Var))
  172. if err != nil {
  173. return nil, fmt.Errorf("deserializing var: %w", err)
  174. }
  175. sw.PutVars(v)
  176. return &agtrpc.SendVarResp{}, nil
  177. }
  178. func (s *Service) GetVar(ctx context.Context, req *agtrpc.GetVarReq) (*agtrpc.GetVarResp, error) {
  179. ctx2, cancel := context.WithTimeout(ctx, time.Second*30)
  180. defer cancel()
  181. sw := s.swWorker.FindByIDContexted(ctx2, exec.PlanID(req.PlanID))
  182. if sw == nil {
  183. return nil, fmt.Errorf("plan not found")
  184. }
  185. v, err := serder.JSONToObjectEx[exec.Var]([]byte(req.Var))
  186. if err != nil {
  187. return nil, fmt.Errorf("deserializing var: %w", err)
  188. }
  189. signal, err := serder.JSONToObjectEx[*exec.SignalVar]([]byte(req.Signal))
  190. if err != nil {
  191. return nil, fmt.Errorf("deserializing var: %w", err)
  192. }
  193. sw.PutVars(signal)
  194. err = sw.BindVars(ctx, v)
  195. if err != nil {
  196. return nil, fmt.Errorf("binding vars: %w", err)
  197. }
  198. vd, err := serder.ObjectToJSONEx(v)
  199. if err != nil {
  200. return nil, fmt.Errorf("serializing var: %w", err)
  201. }
  202. return &agtrpc.GetVarResp{
  203. Var: string(vd),
  204. }, nil
  205. }

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