github.com/qiniu/go-sdk/v7@v7.27.0/storagev2/region/region.go (about) 1 package region 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "reflect" 8 "strings" 9 "time" 10 11 "github.com/qiniu/go-sdk/v7/internal/freezer" 12 "github.com/qiniu/go-sdk/v7/internal/hostprovider" 13 ) 14 15 type ( 16 // 服务地址 17 // 18 // 可以存储域名或 IP,端口和协议可选 19 Endpoints struct { 20 Preferred []string `json:"preferred,omitempty"` // 首选服务地址 21 Alternative []string `json:"alternative,omitempty"` // 备选服务地址 22 Accelerated []string `json:"accelerated,omitempty"` // 加速服务地址 23 } 24 25 // 区域信息 26 // 27 // 可能有多个机房信息,每个机房可能有多个服务地址 28 // 29 // 如果使用公有云,建议使用 GetRegionByID 方法直接获取获取区域实例,不建议手动设置服务地址 30 Region struct { 31 RegionID string `json:"region_id,omitempty"` // 区域 ID 32 Up Endpoints `json:"up,omitempty"` // Up 服务域名 33 Io Endpoints `json:"io,omitempty"` // Io 服务域名 34 IoSrc Endpoints `json:"io_src,omitempty"` // Io 源站服务域名 35 Rs Endpoints `json:"rs,omitempty"` // Rs 服务域名 36 Rsf Endpoints `json:"rsf,omitempty"` // Rsf 服务域名 37 Api Endpoints `json:"api,omitempty"` // Api 服务域名 38 Bucket Endpoints `json:"bucket,omitempty"` // Bucket 服务域名 39 } 40 41 // 区域提供者 42 RegionsProvider interface { 43 GetRegions(context.Context) ([]*Region, error) 44 } 45 46 // 服务名称 47 ServiceName string 48 49 endpointsStatus uint8 50 51 // 服务地址迭代器 52 EndpointsIter struct { 53 endpoints Endpoints 54 index int 55 current endpointsStatus 56 } 57 58 // 服务地址提供者 59 EndpointsProvider interface { 60 GetEndpoints(context.Context) (Endpoints, error) 61 } 62 63 endpointsHostProvider struct { 64 iter *EndpointsIter 65 freezer freezer.Freezer 66 lastFreezeErr error 67 } 68 ) 69 70 const ( 71 endpointsStatusAccelerated endpointsStatus = iota 72 endpointsStatusPreferred 73 endpointsStatusAlternative 74 ) 75 76 const ( 77 // Up 服务 78 ServiceUp ServiceName = "up" 79 // Io 服务 80 ServiceIo ServiceName = "io" 81 // Io 源站服务 82 ServiceIoSrc ServiceName = "io_src" 83 // Rs 服务 84 ServiceRs ServiceName = "rs" 85 // Rsf 服务 86 ServiceRsf ServiceName = "rsf" 87 // Api 服务 88 ServiceApi ServiceName = "api" 89 // Bucket 服务 90 ServiceBucket ServiceName = "bucket" 91 ) 92 93 var ErrUnrecognizedServiceName = errors.New("unrecognized service name") 94 95 // 根据 RegionID 获取公有云区域信息 96 func GetRegionByID(regionID string, useHttps bool) *Region { 97 region := &Region{RegionID: regionID} 98 if regionID == "z0" { 99 region.Up.Preferred = []string{makeHost("upload.qiniup.com", useHttps), makeHost("upload-z0.qiniup.com", useHttps), makeHost("up.qiniup.com", useHttps), makeHost("up-z0.qiniup.com", useHttps)} 100 region.Up.Alternative = []string{makeHost("up.qbox.me", useHttps), makeHost("up-z0.qbox.me", useHttps)} 101 region.Io.Preferred = []string{makeHost("iovip.qiniuio.com", useHttps), makeHost("iovip-z0.qiniuio.com", useHttps)} 102 region.Io.Alternative = []string{makeHost("iovip.qbox.me", useHttps), makeHost("iovip-z0.qbox.me", useHttps)} 103 } else { 104 region.Up.Preferred = []string{makeHost(fmt.Sprintf("upload-%s.qiniup.com", regionID), useHttps), makeHost(fmt.Sprintf("up-%s.qiniup.com", regionID), useHttps)} 105 region.Io.Preferred = []string{makeHost(fmt.Sprintf("iovip-%s.qiniuio.com", regionID), useHttps)} 106 } 107 region.Rs.Preferred = []string{makeHost(fmt.Sprintf("rs-%s.qiniuapi.com", regionID), useHttps)} 108 region.Rsf.Preferred = []string{makeHost(fmt.Sprintf("rsf-%s.qiniuapi.com", regionID), useHttps)} 109 region.Api.Preferred = []string{makeHost(fmt.Sprintf("api-%s.qiniuapi.com", regionID), useHttps)} 110 region.Bucket.Preferred = []string{makeHost("uc.qiniuapi.com", useHttps), makeHost("kodo-config.qiniuapi.com", useHttps)} 111 region.Bucket.Alternative = []string{makeHost("uc.qbox.me", useHttps)} 112 return region 113 } 114 115 func makeHost(domain string, useHttps bool) string { 116 if useHttps { 117 return "https://" + domain 118 } else { 119 return "http://" + domain 120 } 121 } 122 123 func (r *Region) GetRegions(context.Context) ([]*Region, error) { 124 return []*Region{r}, nil 125 } 126 127 func (r *Region) Endpoints(serviceNames []ServiceName) (Endpoints, error) { 128 var endpoint Endpoints 129 for _, serviceName := range serviceNames { 130 switch serviceName { 131 case ServiceUp: 132 endpoint = endpoint.Join(r.Up) 133 case ServiceIo: 134 endpoint = endpoint.Join(r.Io) 135 case ServiceIoSrc: 136 endpoint = endpoint.Join(r.IoSrc) 137 case ServiceRs: 138 endpoint = endpoint.Join(r.Rs) 139 case ServiceRsf: 140 endpoint = endpoint.Join(r.Rsf) 141 case ServiceApi: 142 endpoint = endpoint.Join(r.Api) 143 case ServiceBucket: 144 endpoint = endpoint.Join(r.Bucket) 145 default: 146 return endpoint, ErrUnrecognizedServiceName 147 } 148 } 149 return endpoint, nil 150 } 151 152 func (r *Region) EndpointsIter(serviceNames []ServiceName) (*EndpointsIter, error) { 153 endpoints, err := r.Endpoints(serviceNames) 154 if err != nil { 155 return nil, err 156 } 157 return endpoints.Iter(), nil 158 } 159 160 func (r *Region) IsEqual(right *Region) bool { 161 return r.RegionID == right.RegionID && 162 r.Up.IsEqual(right.Up) && 163 r.Io.IsEqual(right.Io) && 164 r.IoSrc.IsEqual(right.IoSrc) && 165 r.Rs.IsEqual(right.Rs) && 166 r.Rsf.IsEqual(right.Rsf) && 167 r.Api.IsEqual(right.Api) && 168 r.Bucket.IsEqual(right.Bucket) 169 } 170 171 func (ep Endpoints) Join(rights ...Endpoints) Endpoints { 172 newEndpoint := ep 173 for _, right := range rights { 174 newEndpoint.Accelerated = append(newEndpoint.Accelerated, right.Accelerated...) 175 newEndpoint.Preferred = append(newEndpoint.Preferred, right.Preferred...) 176 newEndpoint.Alternative = append(newEndpoint.Alternative, right.Alternative...) 177 } 178 179 return newEndpoint 180 } 181 182 func (ep Endpoints) IsEqual(right Endpoints) bool { 183 return reflect.DeepEqual(ep.Accelerated, right.Accelerated) && 184 reflect.DeepEqual(ep.Preferred, right.Preferred) && 185 reflect.DeepEqual(ep.Alternative, right.Alternative) 186 } 187 188 func (ep Endpoints) Iter() *EndpointsIter { 189 return &EndpointsIter{endpoints: ep} 190 } 191 192 func (ep Endpoints) HostsLength() int { 193 return len(ep.Accelerated) + len(ep.Preferred) + len(ep.Alternative) 194 } 195 196 func (ep Endpoints) IsEmpty() bool { 197 return len(ep.Accelerated) == 0 && len(ep.Preferred) == 0 && len(ep.Alternative) == 0 198 } 199 200 func (ep Endpoints) firstUrl(useHttps bool) string { 201 for _, accelerated := range ep.Accelerated { 202 return makeUrlFromHost(accelerated, useHttps) 203 } 204 for _, preferred := range ep.Preferred { 205 return makeUrlFromHost(preferred, useHttps) 206 } 207 for _, alternative := range ep.Alternative { 208 return makeUrlFromHost(alternative, useHttps) 209 } 210 return "" 211 } 212 213 func (ep Endpoints) GetEndpoints(context.Context) (Endpoints, error) { 214 return ep, nil 215 } 216 217 func (ep Endpoints) allUrls(useHttps bool) []string { 218 allHosts := make([]string, 0, len(ep.Accelerated)+len(ep.Preferred)+len(ep.Alternative)) 219 for _, accelerated := range ep.Accelerated { 220 allHosts = append(allHosts, makeUrlFromHost(accelerated, useHttps)) 221 } 222 for _, preferred := range ep.Preferred { 223 allHosts = append(allHosts, makeUrlFromHost(preferred, useHttps)) 224 } 225 for _, alternative := range ep.Alternative { 226 allHosts = append(allHosts, makeUrlFromHost(alternative, useHttps)) 227 } 228 return allHosts 229 } 230 231 func (ep Endpoints) ToHostProvider() hostprovider.HostProvider { 232 return &endpointsHostProvider{ 233 iter: ep.Iter(), 234 freezer: freezer.New(), 235 } 236 } 237 238 func (ep Endpoints) Clone() Endpoints { 239 return Endpoints{ 240 Preferred: append([]string{}, ep.Preferred...), 241 Alternative: append([]string{}, ep.Alternative...), 242 Accelerated: append([]string{}, ep.Accelerated...), 243 } 244 } 245 246 func makeUrlFromHost(host string, useHttps bool) string { 247 if strings.HasPrefix(host, "http://") || strings.HasPrefix(host, "https://") { 248 return host 249 } 250 if useHttps { 251 return "https://" + host 252 } else { 253 return "http://" + host 254 } 255 } 256 257 func (iter *EndpointsIter) Next(nextHost *string) bool { 258 for { 259 switch iter.current { 260 case endpointsStatusAccelerated: 261 if iter.index >= len(iter.endpoints.Accelerated) { 262 iter.current = endpointsStatusPreferred 263 iter.index = 0 264 continue 265 } 266 host := iter.endpoints.Accelerated[iter.index] 267 iter.index += 1 268 *nextHost = host 269 return true 270 case endpointsStatusPreferred: 271 if iter.index >= len(iter.endpoints.Preferred) { 272 iter.current = endpointsStatusAlternative 273 iter.index = 0 274 continue 275 } 276 host := iter.endpoints.Preferred[iter.index] 277 iter.index += 1 278 *nextHost = host 279 return true 280 case endpointsStatusAlternative: 281 if iter.index >= len(iter.endpoints.Alternative) { 282 return false 283 } 284 host := iter.endpoints.Alternative[iter.index] 285 iter.index += 1 286 *nextHost = host 287 return true 288 default: 289 return false 290 } 291 } 292 } 293 294 func (iter *EndpointsIter) More() bool { 295 switch iter.current { 296 case endpointsStatusAccelerated: 297 if iter.index >= len(iter.endpoints.Accelerated) { 298 return len(iter.endpoints.Preferred) > 0 299 } 300 return true 301 case endpointsStatusPreferred: 302 if iter.index >= len(iter.endpoints.Preferred) { 303 return len(iter.endpoints.Alternative) > 0 304 } 305 return true 306 case endpointsStatusAlternative: 307 return iter.index < len(iter.endpoints.Alternative) 308 default: 309 return false 310 } 311 } 312 313 func (iter *EndpointsIter) SwitchToAlternative() { 314 if len(iter.endpoints.Alternative) > 0 && iter.current != endpointsStatusAlternative { 315 iter.current = endpointsStatusAlternative 316 iter.index = 0 317 } 318 } 319 320 func (iter *EndpointsIter) SwitchToPreferred() { 321 if len(iter.endpoints.Preferred) > 0 && iter.current != endpointsStatusPreferred { 322 iter.current = endpointsStatusPreferred 323 iter.index = 0 324 } 325 } 326 327 func (provider *endpointsHostProvider) Freeze(host string, cause error, duration time.Duration) error { 328 if duration <= 0 { 329 return nil 330 } 331 332 provider.lastFreezeErr = cause 333 return provider.freezer.Freeze(host, duration) 334 } 335 336 func (provider *endpointsHostProvider) Provider() (string, error) { 337 if provider.iter.endpoints.IsEmpty() { 338 return "", hostprovider.ErrNoHostFound 339 } 340 341 var host string 342 for provider.iter.Next(&host) { 343 if provider.freezer.Available(host) { 344 return host, nil 345 } 346 } 347 348 if provider.lastFreezeErr != nil { 349 return "", provider.lastFreezeErr 350 } else { 351 return "", hostprovider.ErrAllHostsFrozen 352 } 353 }