您最多选择25个标签 标签必须以中文、字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. package plans
  2. import (
  3. "context"
  4. "errors"
  5. "fmt"
  6. "io"
  7. "sync"
  8. "sync/atomic"
  9. "gitlink.org.cn/cloudream/common/pkgs/future"
  10. "gitlink.org.cn/cloudream/common/utils/io2"
  11. stgglb "gitlink.org.cn/cloudream/storage/common/globals"
  12. "gitlink.org.cn/cloudream/storage/common/pkgs/ioswitch"
  13. agtmq "gitlink.org.cn/cloudream/storage/common/pkgs/mq/agent"
  14. )
  15. type ExecutorResult struct {
  16. ResultValues map[string]any
  17. }
  18. type Executor struct {
  19. plan ComposedPlan
  20. callback *future.SetValueFuture[ExecutorResult]
  21. mqClis []*agtmq.Client
  22. planTaskIDs []string
  23. }
  24. func Execute(plan ComposedPlan) (*Executor, error) {
  25. executor := Executor{
  26. plan: plan,
  27. callback: future.NewSetValue[ExecutorResult](),
  28. }
  29. var err error
  30. for _, a := range plan.AgentPlans {
  31. var cli *agtmq.Client
  32. cli, err = stgglb.AgentMQPool.Acquire(a.Node.NodeID)
  33. if err != nil {
  34. executor.Close()
  35. return nil, fmt.Errorf("new mq client for %d: %w", a.Node.NodeID, err)
  36. }
  37. executor.mqClis = append(executor.mqClis, cli)
  38. }
  39. for i, a := range plan.AgentPlans {
  40. cli := executor.mqClis[i]
  41. _, err := cli.SetupIOPlan(agtmq.NewSetupIOPlan(a.Plan))
  42. if err != nil {
  43. for i -= 1; i >= 0; i-- {
  44. executor.mqClis[i].CancelIOPlan(agtmq.NewCancelIOPlan(plan.ID))
  45. }
  46. executor.Close()
  47. return nil, fmt.Errorf("setup plan at %d: %w", a.Node.NodeID, err)
  48. }
  49. }
  50. for i, a := range plan.AgentPlans {
  51. cli := executor.mqClis[i]
  52. resp, err := cli.StartIOPlan(agtmq.NewStartIOPlan(a.Plan.ID))
  53. if err != nil {
  54. executor.cancelAll()
  55. executor.Close()
  56. return nil, fmt.Errorf("setup plan at %d: %w", a.Node.NodeID, err)
  57. }
  58. executor.planTaskIDs = append(executor.planTaskIDs, resp.TaskID)
  59. }
  60. go executor.pollResult()
  61. return &executor, nil
  62. }
  63. func (e *Executor) SendStream(info *FromExecutorStream, stream io.Reader) error {
  64. // TODO 考虑不使用stgglb的Local
  65. nodeIP := info.toNode.ExternalIP
  66. grpcPort := info.toNode.ExternalGRPCPort
  67. if info.toNode.LocationID == stgglb.Local.LocationID {
  68. nodeIP = info.toNode.LocalIP
  69. grpcPort = info.toNode.LocalGRPCPort
  70. }
  71. agtCli, err := stgglb.AgentRPCPool.Acquire(nodeIP, grpcPort)
  72. if err != nil {
  73. return fmt.Errorf("new agent rpc client: %w", err)
  74. }
  75. defer stgglb.AgentRPCPool.Release(agtCli)
  76. return agtCli.SendStream(e.plan.ID, info.info.ID, stream)
  77. }
  78. func (e *Executor) ReadStream(info *ToExecutorStream) (io.ReadCloser, error) {
  79. // TODO 考虑不使用stgglb的Local
  80. nodeIP := info.fromNode.ExternalIP
  81. grpcPort := info.fromNode.ExternalGRPCPort
  82. if info.fromNode.LocationID == stgglb.Local.LocationID {
  83. nodeIP = info.fromNode.LocalIP
  84. grpcPort = info.fromNode.LocalGRPCPort
  85. }
  86. agtCli, err := stgglb.AgentRPCPool.Acquire(nodeIP, grpcPort)
  87. if err != nil {
  88. return nil, fmt.Errorf("new agent rpc client: %w", err)
  89. }
  90. str, err := agtCli.FetchStream(e.plan.ID, info.info.ID)
  91. if err != nil {
  92. return nil, err
  93. }
  94. return io2.AfterReadClosed(str, func(closer io.ReadCloser) {
  95. stgglb.AgentRPCPool.Release(agtCli)
  96. }), nil
  97. }
  98. func (e *Executor) Wait() (ExecutorResult, error) {
  99. return e.callback.WaitValue(context.TODO())
  100. }
  101. func (e *Executor) cancelAll() {
  102. for _, cli := range e.mqClis {
  103. cli.CancelIOPlan(agtmq.NewCancelIOPlan(e.plan.ID))
  104. }
  105. }
  106. func (e *Executor) Close() {
  107. for _, c := range e.mqClis {
  108. stgglb.AgentMQPool.Release(c)
  109. }
  110. }
  111. func (e *Executor) pollResult() {
  112. wg := sync.WaitGroup{}
  113. var anyErr error
  114. var done atomic.Bool
  115. rets := make([]*ioswitch.PlanResult, len(e.plan.AgentPlans))
  116. for i, id := range e.planTaskIDs {
  117. idx := i
  118. taskID := id
  119. wg.Add(1)
  120. go func() {
  121. defer wg.Done()
  122. for {
  123. resp, err := e.mqClis[idx].WaitIOPlan(agtmq.NewWaitIOPlan(taskID, 5000))
  124. if err != nil {
  125. anyErr = err
  126. break
  127. }
  128. if resp.IsComplete {
  129. if resp.Error != "" {
  130. anyErr = errors.New(resp.Error)
  131. done.Store(true)
  132. } else {
  133. rets[idx] = &resp.Result
  134. }
  135. break
  136. }
  137. if done.Load() {
  138. break
  139. }
  140. }
  141. }()
  142. }
  143. wg.Wait()
  144. if anyErr != nil {
  145. e.callback.SetError(anyErr)
  146. return
  147. }
  148. reducedRet := ExecutorResult{
  149. ResultValues: make(map[string]any),
  150. }
  151. for _, ret := range rets {
  152. for k, v := range ret.Values {
  153. reducedRet.ResultValues[k] = v
  154. }
  155. }
  156. e.callback.SetValue(reducedRet)
  157. }

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