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.

uploader.go 4.8 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. package uploader
  2. import (
  3. "fmt"
  4. "math"
  5. "math/rand"
  6. "time"
  7. "github.com/samber/lo"
  8. cdssdk "gitlink.org.cn/cloudream/common/sdks/storage"
  9. "gitlink.org.cn/cloudream/common/utils/sort2"
  10. stgglb "gitlink.org.cn/cloudream/storage/common/globals"
  11. stgmod "gitlink.org.cn/cloudream/storage/common/models"
  12. "gitlink.org.cn/cloudream/storage/common/pkgs/connectivity"
  13. "gitlink.org.cn/cloudream/storage/common/pkgs/distlock"
  14. "gitlink.org.cn/cloudream/storage/common/pkgs/distlock/reqbuilder"
  15. coormq "gitlink.org.cn/cloudream/storage/common/pkgs/mq/coordinator"
  16. "gitlink.org.cn/cloudream/storage/common/pkgs/storage/svcmgr"
  17. )
  18. type Uploader struct {
  19. distlock *distlock.Service
  20. connectivity *connectivity.Collector
  21. stgMgr *svcmgr.Manager
  22. }
  23. func NewUploader(distlock *distlock.Service, connectivity *connectivity.Collector, stgMgr *svcmgr.Manager) *Uploader {
  24. return &Uploader{
  25. distlock: distlock,
  26. connectivity: connectivity,
  27. stgMgr: stgMgr,
  28. }
  29. }
  30. func (u *Uploader) BeginUpdate(userID cdssdk.UserID, pkgID cdssdk.PackageID, affinity cdssdk.StorageID) (*UpdateUploader, error) {
  31. coorCli, err := stgglb.CoordinatorMQPool.Acquire()
  32. if err != nil {
  33. return nil, fmt.Errorf("new coordinator client: %w", err)
  34. }
  35. defer stgglb.CoordinatorMQPool.Release(coorCli)
  36. getUserStgsResp, err := coorCli.GetUserStorageDetails(coormq.ReqGetUserStorageDetails(userID))
  37. if err != nil {
  38. return nil, fmt.Errorf("getting user storages: %w", err)
  39. }
  40. cons := u.connectivity.GetAll()
  41. var userStgs []UploadStorageInfo
  42. for _, stg := range getUserStgsResp.Storages {
  43. if stg.MasterHub == nil {
  44. continue
  45. }
  46. delay := time.Duration(math.MaxInt64)
  47. con, ok := cons[stg.MasterHub.HubID]
  48. if ok && con.Delay != nil {
  49. delay = *con.Delay
  50. }
  51. userStgs = append(userStgs, UploadStorageInfo{
  52. Storage: stg,
  53. Delay: delay,
  54. IsSameLocation: stg.MasterHub.LocationID == stgglb.Local.LocationID,
  55. })
  56. }
  57. if len(userStgs) == 0 {
  58. return nil, fmt.Errorf("user no available storages")
  59. }
  60. target := u.chooseUploadStorage(userStgs, affinity)
  61. // 给上传节点的IPFS加锁
  62. // TODO 考虑加Object的Create锁
  63. // 防止上传的副本被清除
  64. distMutex, err := reqbuilder.NewBuilder().Shard().Buzy(target.Storage.Storage.StorageID).MutexLock(u.distlock)
  65. if err != nil {
  66. return nil, fmt.Errorf("acquire distlock: %w", err)
  67. }
  68. return &UpdateUploader{
  69. uploader: u,
  70. pkgID: pkgID,
  71. targetStg: target.Storage,
  72. distMutex: distMutex,
  73. }, nil
  74. }
  75. // chooseUploadStorage 选择一个上传文件的节点
  76. // 1. 选择设置了亲和性的节点
  77. // 2. 从与当前客户端相同地域的节点中随机选一个
  78. // 3. 没有的话从所有节点选择延迟最低的节点
  79. func (w *Uploader) chooseUploadStorage(storages []UploadStorageInfo, stgAffinity cdssdk.StorageID) UploadStorageInfo {
  80. if stgAffinity > 0 {
  81. aff, ok := lo.Find(storages, func(storage UploadStorageInfo) bool { return storage.Storage.Storage.StorageID == stgAffinity })
  82. if ok {
  83. return aff
  84. }
  85. }
  86. sameLocationStorages := lo.Filter(storages, func(e UploadStorageInfo, i int) bool { return e.IsSameLocation })
  87. if len(sameLocationStorages) > 0 {
  88. return sameLocationStorages[rand.Intn(len(sameLocationStorages))]
  89. }
  90. // 选择延迟最低的节点
  91. storages = sort2.Sort(storages, func(e1, e2 UploadStorageInfo) int { return sort2.Cmp(e1.Delay, e2.Delay) })
  92. return storages[0]
  93. }
  94. func (u *Uploader) BeginCreateLoad(userID cdssdk.UserID, bktID cdssdk.BucketID, pkgName string, loadTo []cdssdk.StorageID) (*CreateLoadUploader, error) {
  95. coorCli, err := stgglb.CoordinatorMQPool.Acquire()
  96. if err != nil {
  97. return nil, fmt.Errorf("new coordinator client: %w", err)
  98. }
  99. defer stgglb.CoordinatorMQPool.Release(coorCli)
  100. getStgs, err := coorCli.GetStorageDetails(coormq.ReqGetStorageDetails(loadTo))
  101. if err != nil {
  102. return nil, fmt.Errorf("getting storages: %w", err)
  103. }
  104. targetStgs := make([]stgmod.StorageDetail, len(loadTo))
  105. for i, stg := range getStgs.Storages {
  106. if stg == nil {
  107. return nil, fmt.Errorf("storage %v not found", loadTo[i])
  108. }
  109. targetStgs[i] = *stg
  110. }
  111. createPkg, err := coorCli.CreatePackage(coormq.NewCreatePackage(userID, bktID, pkgName))
  112. if err != nil {
  113. return nil, fmt.Errorf("create package: %w", err)
  114. }
  115. reqBld := reqbuilder.NewBuilder()
  116. for _, stg := range targetStgs {
  117. reqBld.Shard().Buzy(stg.Storage.StorageID)
  118. reqBld.Storage().Buzy(stg.Storage.StorageID)
  119. reqBld.Metadata().StoragePackage().CreateOne(userID, stg.Storage.StorageID, createPkg.Package.PackageID)
  120. }
  121. lock, err := reqBld.MutexLock(u.distlock)
  122. if err != nil {
  123. return nil, fmt.Errorf("acquire distlock: %w", err)
  124. }
  125. return &CreateLoadUploader{
  126. pkg: createPkg.Package,
  127. userID: userID,
  128. targetStgs: targetStgs,
  129. uploader: u,
  130. distlock: lock,
  131. }, nil
  132. }

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