github.com/qiniu/go-sdk/v7@v7.27.0/storage/region_uc_v4.go (about) 1 package storage 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "math" 8 "os" 9 "path/filepath" 10 "sync" 11 "time" 12 13 "golang.org/x/sync/singleflight" 14 15 "github.com/qiniu/go-sdk/v7/storagev2/apis" 16 "github.com/qiniu/go-sdk/v7/storagev2/apis/query_bucket_v4" 17 ) 18 19 var ucQueryV4Group singleflight.Group 20 21 type regionV4CacheValue struct { 22 Regions []*Region `json:"regions"` 23 Deadline time.Time `json:"deadline"` 24 } 25 26 func (r *regionV4CacheValue) getRegions() []*Region { 27 if r == nil { 28 return nil 29 } 30 return r.Regions 31 } 32 33 type regionV4CacheMap map[string]regionV4CacheValue 34 35 const regionV4CacheFileName = "query_v4_00.cache.json" 36 37 var ( 38 regionV4CachePath = filepath.Join(os.TempDir(), "qiniu-golang-sdk", regionV4CacheFileName) 39 regionV4Cache sync.Map 40 regionV4CacheLock sync.RWMutex 41 regionV4CacheSyncLock sync.Mutex 42 regionV4CacheLoaded bool = false 43 ) 44 45 func setRegionV4CachePath(newPath string) { 46 cacheDir := filepath.Dir(newPath) 47 if len(cacheDir) == 0 { 48 return 49 } 50 51 regionV4CacheLock.Lock() 52 defer regionV4CacheLock.Unlock() 53 54 regionV4CachePath = filepath.Join(cacheDir, regionV4CacheFileName) 55 regionV4CacheLoaded = false 56 } 57 58 func loadRegionV4Cache() { 59 cacheFile, err := os.Open(regionV4CachePath) 60 if err != nil { 61 return 62 } 63 defer cacheFile.Close() 64 65 var cacheMap regionV4CacheMap 66 if err = json.NewDecoder(cacheFile).Decode(&cacheMap); err != nil { 67 return 68 } 69 for cacheKey, cacheValue := range cacheMap { 70 regionV4Cache.Store(cacheKey, cacheValue) 71 } 72 } 73 74 func storeRegionV4Cache() { 75 err := os.MkdirAll(filepath.Dir(regionV4CachePath), 0700) 76 if err != nil { 77 return 78 } 79 80 cacheFile, err := os.OpenFile(regionV4CachePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600) 81 if err != nil { 82 return 83 } 84 defer cacheFile.Close() 85 86 cacheMap := make(regionV4CacheMap) 87 regionV4Cache.Range(func(cacheKey, cacheValue interface{}) bool { 88 cacheMap[cacheKey.(string)] = cacheValue.(regionV4CacheValue) 89 return true 90 }) 91 if err = json.NewEncoder(cacheFile).Encode(cacheMap); err != nil { 92 return 93 } 94 } 95 96 func getRegionByV4(ak, bucket string, options UCApiOptions) (*RegionGroup, error) { 97 regionV4CacheLock.RLock() 98 if regionV4CacheLoaded { 99 regionV4CacheLock.RUnlock() 100 } else { 101 regionV4CacheLock.RUnlock() 102 func() { 103 regionV4CacheLock.Lock() 104 defer regionV4CacheLock.Unlock() 105 106 if !regionV4CacheLoaded { 107 loadRegionV4Cache() 108 regionV4CacheLoaded = true 109 } 110 }() 111 } 112 113 regionCacheKey := makeRegionCacheKey(ak, bucket, options.Hosts, options.AccelerateUploading) 114 // check from cache 115 if v, ok := regionV4Cache.Load(regionCacheKey); ok && time.Now().Before(v.(regionV4CacheValue).Deadline) { 116 cacheValue, _ := v.(regionV4CacheValue) 117 return NewRegionGroup(cacheValue.getRegions()...), nil 118 } 119 120 newRegion, err, _ := ucQueryV4Group.Do(regionCacheKey, func() (interface{}, error) { 121 regions, ttl, err := _getRegionByV4WithoutCache(ak, bucket, options) 122 if err != nil { 123 return nil, fmt.Errorf("query region error, %s", err.Error()) 124 } 125 regionV4Cache.Store(regionCacheKey, regionV4CacheValue{ 126 Regions: regions, 127 Deadline: time.Now().Add(time.Duration(ttl) * time.Second), 128 }) 129 130 regionV4CacheSyncLock.Lock() 131 defer regionV4CacheSyncLock.Unlock() 132 133 storeRegionV4Cache() 134 135 return NewRegionGroup(regions...), nil 136 }) 137 138 if newRegion == nil { 139 return nil, err 140 } 141 142 return newRegion.(*RegionGroup), err 143 } 144 145 func _getRegionByV4WithoutCache(ak, bucket string, options UCApiOptions) ([]*Region, int64, error) { 146 toRegion := func(r *query_bucket_v4.BucketQueryHost) *Region { 147 var rsHost, rsfHost, apiHost, ioVipHost, ioSrcHost string 148 upDomains := make([]string, 0, len(r.UpDomains.AcceleratedUpDomains)+len(r.UpDomains.PreferedUpDomains)+len(r.UpDomains.AlternativeUpDomains)) 149 if options.AccelerateUploading && len(r.UpDomains.AcceleratedUpDomains) > 0 { 150 upDomains = append(upDomains, r.UpDomains.AcceleratedUpDomains...) 151 } 152 upDomains = append(upDomains, r.UpDomains.PreferedUpDomains...) 153 upDomains = append(upDomains, r.UpDomains.AlternativeUpDomains...) 154 if len(r.RsDomains.PreferedRsDomains) > 0 { 155 rsHost = r.RsDomains.PreferedRsDomains[0] 156 } 157 if len(r.RsfDomains.PreferedRsfDomains) > 0 { 158 rsfHost = r.RsfDomains.PreferedRsfDomains[0] 159 } 160 if len(r.ApiDomains.PreferedApiDomains) > 0 { 161 apiHost = r.ApiDomains.PreferedApiDomains[0] 162 } 163 if len(r.IoDomains.PreferedIoDomains) > 0 { 164 ioVipHost = r.IoDomains.PreferedIoDomains[0] 165 } 166 if len(r.IoSrcDomains.PreferedIoSrcDomains) > 0 { 167 ioSrcHost = r.IoSrcDomains.PreferedIoSrcDomains[0] 168 } 169 return &Region{ 170 SrcUpHosts: upDomains, 171 CdnUpHosts: upDomains, 172 RsHost: rsHost, 173 RsfHost: rsfHost, 174 ApiHost: apiHost, 175 IovipHost: ioVipHost, 176 IoSrcHost: ioSrcHost, 177 } 178 } 179 response, err := options.getApiStorageClient().QueryBucketV4( 180 context.Background(), 181 &apis.QueryBucketV4Request{ 182 Bucket: bucket, 183 AccessKey: ak, 184 }, 185 options.getApiOptions(), 186 ) 187 if err != nil { 188 return nil, 0, err 189 } 190 regions := make([]*Region, 0, len(response.Hosts)) 191 var ttl int64 = math.MaxInt64 192 for _, host := range response.Hosts { 193 regions = append(regions, toRegion(&host)) 194 if ttl > host.TimeToLive { 195 ttl = host.TimeToLive 196 } 197 } 198 return regions, ttl, nil 199 }