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.

update_rep_object.go 4.1 kB

2 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. package task
  2. import (
  3. "fmt"
  4. "io"
  5. "time"
  6. "gitlink.org.cn/cloudream/common/pkgs/distlock/reqbuilder"
  7. "gitlink.org.cn/cloudream/common/pkgs/logger"
  8. mysort "gitlink.org.cn/cloudream/common/utils/sort"
  9. "gitlink.org.cn/cloudream/storage-client/internal/config"
  10. coormsg "gitlink.org.cn/cloudream/storage-common/pkgs/mq/message/coordinator"
  11. )
  12. type UpdateRepObject struct {
  13. userID int64
  14. objectID int64
  15. file io.ReadCloser
  16. fileSize int64
  17. }
  18. func NewUpdateRepObject(userID int64, objectID int64, file io.ReadCloser, fileSize int64) *UpdateRepObject {
  19. return &UpdateRepObject{
  20. userID: userID,
  21. objectID: objectID,
  22. file: file,
  23. fileSize: fileSize,
  24. }
  25. }
  26. func (t *UpdateRepObject) Execute(ctx TaskContext, complete CompleteFn) {
  27. err := t.do(ctx)
  28. complete(err, CompleteOption{
  29. RemovingDelay: time.Minute,
  30. })
  31. }
  32. func (t *UpdateRepObject) do(ctx TaskContext) error {
  33. mutex, err := reqbuilder.NewBuilder().
  34. Metadata().
  35. // 用于判断用户是否有对象的权限
  36. UserBucket().ReadAny().
  37. // 用于读取、修改对象信息
  38. Object().WriteOne(t.objectID).
  39. // 用于更新Rep配置
  40. ObjectRep().WriteOne(t.objectID).
  41. // 用于查询可用的上传节点
  42. Node().ReadAny().
  43. // 用于创建Cache记录
  44. Cache().CreateAny().
  45. // 用于修改Move此Object的记录的状态
  46. StorageObject().WriteAny().
  47. MutexLock(ctx.DistLock)
  48. if err != nil {
  49. return fmt.Errorf("acquire locks failed, err: %w", err)
  50. }
  51. defer mutex.Unlock()
  52. preResp, err := ctx.Coordinator.PreUpdateRepObject(coormsg.NewPreUpdateRepObject(
  53. t.objectID,
  54. t.fileSize,
  55. t.userID,
  56. config.Cfg().ExternalIP,
  57. ))
  58. if err != nil {
  59. return fmt.Errorf("pre update rep object: %w", err)
  60. }
  61. if len(preResp.Nodes) == 0 {
  62. return fmt.Errorf("no node to upload file")
  63. }
  64. // 上传文件的方式优先级:
  65. // 1. 本地IPFS
  66. // 2. 包含了旧文件,且与客户端在同地域的节点
  67. // 3. 不在同地域,但包含了旧文件的节点
  68. // 4. 同地域节点
  69. uploadNode := t.chooseUpdateRepObjectNode(preResp.Nodes)
  70. var fileHash string
  71. uploadedNodeIDs := []int64{}
  72. willUploadToNode := true
  73. // 本地有IPFS,则直接从本地IPFS上传
  74. if ctx.IPFS != nil {
  75. logger.Infof("try to use local IPFS to upload file")
  76. fileHash, err = uploadToLocalIPFS(ctx.IPFS, t.file, uploadNode.ID)
  77. if err != nil {
  78. logger.Warnf("upload to local IPFS failed, so try to upload to node %d, err: %s", uploadNode.ID, err.Error())
  79. } else {
  80. willUploadToNode = false
  81. }
  82. }
  83. // 否则发送到agent上传
  84. if willUploadToNode {
  85. // 如果客户端与节点在同一个地域,则使用内网地址连接节点
  86. nodeIP := uploadNode.ExternalIP
  87. if uploadNode.IsSameLocation {
  88. nodeIP = uploadNode.LocalIP
  89. logger.Infof("client and node %d are at the same location, use local ip\n", uploadNode.ID)
  90. }
  91. mutex, err := reqbuilder.NewBuilder().
  92. IPFS().
  93. // 防止上传的副本被清除
  94. CreateAnyRep(uploadNode.ID).
  95. MutexLock(ctx.DistLock)
  96. if err != nil {
  97. return fmt.Errorf("acquire locks failed, err: %w", err)
  98. }
  99. defer mutex.Unlock()
  100. fileHash, err = uploadToNode(t.file, nodeIP)
  101. if err != nil {
  102. return fmt.Errorf("upload to node %s failed, err: %w", nodeIP, err)
  103. }
  104. uploadedNodeIDs = append(uploadedNodeIDs, uploadNode.ID)
  105. }
  106. // 更新Object
  107. _, err = ctx.Coordinator.UpdateRepObject(coormsg.NewUpdateRepObject(t.objectID, fileHash, t.fileSize, uploadedNodeIDs, t.userID))
  108. if err != nil {
  109. return fmt.Errorf("updating rep object: %w", err)
  110. }
  111. return nil
  112. }
  113. // chooseUploadNode 选择一个上传文件的节点
  114. // 1. 从与当前客户端相同地域的节点中随机选一个
  115. // 2. 没有用的话从所有节点中随机选一个
  116. func (t *UpdateRepObject) chooseUpdateRepObjectNode(nodes []coormsg.PreUpdateRepObjectRespNode) coormsg.PreUpdateRepObjectRespNode {
  117. mysort.Sort(nodes, func(left, right coormsg.PreUpdateRepObjectRespNode) int {
  118. v := -mysort.CmpBool(left.HasOldObject, right.HasOldObject)
  119. if v != 0 {
  120. return v
  121. }
  122. return -mysort.CmpBool(left.IsSameLocation, right.IsSameLocation)
  123. })
  124. return nodes[0]
  125. }

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