gitee.com/sasukebo/go-micro/v4@v4.7.1/registry/cache/cache.go (about) 1 // Package cache provides a registry cache 2 package cache 3 4 import ( 5 "math" 6 "math/rand" 7 "sync" 8 "time" 9 10 "gitee.com/sasukebo/go-micro/v4/logger" 11 "gitee.com/sasukebo/go-micro/v4/registry" 12 util "gitee.com/sasukebo/go-micro/v4/util/registry" 13 "golang.org/x/sync/singleflight" 14 ) 15 16 // Cache is the registry cache interface 17 type Cache interface { 18 // embed the registry interface 19 registry.Registry 20 // stop the cache watcher 21 Stop() 22 } 23 24 type Options struct { 25 // TTL is the cache TTL 26 TTL time.Duration 27 } 28 29 type Option func(o *Options) 30 31 type cache struct { 32 registry.Registry 33 opts Options 34 35 // registry cache 36 sync.RWMutex 37 cache map[string][]*registry.Service 38 ttls map[string]time.Time 39 watched map[string]bool 40 41 // used to stop the cache 42 exit chan bool 43 44 // indicate whether its running 45 running bool 46 // status of the registry 47 // used to hold onto the cache 48 // in failure state 49 status error 50 // used to prevent cache breakdwon 51 sg singleflight.Group 52 } 53 54 var ( 55 DefaultTTL = time.Minute 56 ) 57 58 func backoff(attempts int) time.Duration { 59 if attempts == 0 { 60 return time.Duration(0) 61 } 62 return time.Duration(math.Pow(10, float64(attempts))) * time.Millisecond 63 } 64 65 func (c *cache) getStatus() error { 66 c.RLock() 67 defer c.RUnlock() 68 return c.status 69 } 70 71 func (c *cache) setStatus(err error) { 72 c.Lock() 73 c.status = err 74 c.Unlock() 75 } 76 77 // isValid checks if the service is valid 78 func (c *cache) isValid(services []*registry.Service, ttl time.Time) bool { 79 // no services exist 80 if len(services) == 0 { 81 return false 82 } 83 84 // ttl is invalid 85 if ttl.IsZero() { 86 return false 87 } 88 89 // time since ttl is longer than timeout 90 if time.Since(ttl) > 0 { 91 return false 92 } 93 94 // ok 95 return true 96 } 97 98 func (c *cache) quit() bool { 99 select { 100 case <-c.exit: 101 return true 102 default: 103 return false 104 } 105 } 106 107 func (c *cache) del(service string) { 108 // don't blow away cache in error state 109 if err := c.status; err != nil { 110 return 111 } 112 // otherwise delete entries 113 delete(c.cache, service) 114 delete(c.ttls, service) 115 } 116 117 func (c *cache) get(service string) ([]*registry.Service, error) { 118 // read lock 119 c.RLock() 120 121 // check the cache first 122 services := c.cache[service] 123 // get cache ttl 124 ttl := c.ttls[service] 125 // make a copy 126 cp := util.Copy(services) 127 128 // got services && within ttl so return cache 129 if c.isValid(cp, ttl) { 130 c.RUnlock() 131 // return services 132 return cp, nil 133 } 134 135 // get does the actual request for a service and cache it 136 get := func(service string, cached []*registry.Service) ([]*registry.Service, error) { 137 // ask the registry 138 val, err, _ := c.sg.Do(service, func() (interface{}, error) { 139 return c.Registry.GetService(service) 140 }) 141 services, _ := val.([]*registry.Service) 142 if err != nil { 143 // check the cache 144 if len(cached) > 0 { 145 // set the error status 146 c.setStatus(err) 147 148 // return the stale cache 149 return cached, nil 150 } 151 // otherwise return error 152 return nil, err 153 } 154 155 // reset the status 156 if err := c.getStatus(); err != nil { 157 c.setStatus(nil) 158 } 159 160 // cache results 161 c.Lock() 162 c.set(service, util.Copy(services)) 163 c.Unlock() 164 165 return services, nil 166 } 167 168 // watch service if not watched 169 _, ok := c.watched[service] 170 171 // unlock the read lock 172 c.RUnlock() 173 174 // check if its being watched 175 if !ok { 176 c.Lock() 177 178 // set to watched 179 c.watched[service] = true 180 181 // only kick it off if not running 182 if !c.running { 183 go c.run(service) 184 } 185 186 c.Unlock() 187 } 188 189 // get and return services 190 return get(service, cp) 191 } 192 193 func (c *cache) set(service string, services []*registry.Service) { 194 c.cache[service] = services 195 c.ttls[service] = time.Now().Add(c.opts.TTL) 196 } 197 198 func (c *cache) update(res *registry.Result) { 199 if res == nil || res.Service == nil { 200 return 201 } 202 203 c.Lock() 204 defer c.Unlock() 205 206 // only save watched services 207 if _, ok := c.watched[res.Service.Name]; !ok { 208 return 209 } 210 211 services, ok := c.cache[res.Service.Name] 212 if !ok { 213 // we're not going to cache anything 214 // unless there was already a lookup 215 return 216 } 217 218 if len(res.Service.Nodes) == 0 { 219 switch res.Action { 220 case "delete": 221 c.del(res.Service.Name) 222 } 223 return 224 } 225 226 // existing service found 227 var service *registry.Service 228 var index int 229 for i, s := range services { 230 if s.Version == res.Service.Version { 231 service = s 232 index = i 233 } 234 } 235 236 switch res.Action { 237 case "create", "update": 238 if service == nil { 239 c.set(res.Service.Name, append(services, res.Service)) 240 return 241 } 242 243 // append old nodes to new service 244 for _, cur := range service.Nodes { 245 var seen bool 246 for _, node := range res.Service.Nodes { 247 if cur.Id == node.Id { 248 seen = true 249 break 250 } 251 } 252 if !seen { 253 res.Service.Nodes = append(res.Service.Nodes, cur) 254 } 255 } 256 257 services[index] = res.Service 258 c.set(res.Service.Name, services) 259 case "delete": 260 if service == nil { 261 return 262 } 263 264 var nodes []*registry.Node 265 266 // filter cur nodes to remove the dead one 267 for _, cur := range service.Nodes { 268 var seen bool 269 for _, del := range res.Service.Nodes { 270 if del.Id == cur.Id { 271 seen = true 272 break 273 } 274 } 275 if !seen { 276 nodes = append(nodes, cur) 277 } 278 } 279 280 // still got nodes, save and return 281 if len(nodes) > 0 { 282 service.Nodes = nodes 283 services[index] = service 284 c.set(service.Name, services) 285 return 286 } 287 288 // zero nodes left 289 290 // only have one thing to delete 291 // nuke the thing 292 if len(services) == 1 { 293 c.del(service.Name) 294 return 295 } 296 297 // still have more than 1 service 298 // check the version and keep what we know 299 var srvs []*registry.Service 300 for _, s := range services { 301 if s.Version != service.Version { 302 srvs = append(srvs, s) 303 } 304 } 305 306 // save 307 c.set(service.Name, srvs) 308 case "override": 309 if service == nil { 310 return 311 } 312 313 c.del(service.Name) 314 } 315 } 316 317 // run starts the cache watcher loop 318 // it creates a new watcher if there's a problem 319 func (c *cache) run(service string) { 320 c.Lock() 321 c.running = true 322 c.Unlock() 323 324 // reset watcher on exit 325 defer func() { 326 c.Lock() 327 c.watched = make(map[string]bool) 328 c.running = false 329 c.Unlock() 330 }() 331 332 var a, b int 333 334 for { 335 // exit early if already dead 336 if c.quit() { 337 return 338 } 339 340 // jitter before starting 341 j := rand.Int63n(100) 342 time.Sleep(time.Duration(j) * time.Millisecond) 343 344 // create new watcher 345 w, err := c.Registry.Watch(registry.WatchService(service)) 346 if err != nil { 347 if c.quit() { 348 return 349 } 350 351 d := backoff(a) 352 c.setStatus(err) 353 354 if a > 3 { 355 if logger.V(logger.DebugLevel, logger.DefaultLogger) { 356 logger.Debug("rcache: ", err, " backing off ", d) 357 } 358 a = 0 359 } 360 361 time.Sleep(d) 362 a++ 363 364 continue 365 } 366 367 // reset a 368 a = 0 369 370 // watch for events 371 if err := c.watch(w); err != nil { 372 if c.quit() { 373 return 374 } 375 376 d := backoff(b) 377 c.setStatus(err) 378 379 if b > 3 { 380 if logger.V(logger.DebugLevel, logger.DefaultLogger) { 381 logger.Debug("rcache: ", err, " backing off ", d) 382 } 383 b = 0 384 } 385 386 time.Sleep(d) 387 b++ 388 389 continue 390 } 391 392 // reset b 393 b = 0 394 } 395 } 396 397 // watch loops the next event and calls update 398 // it returns if there's an error 399 func (c *cache) watch(w registry.Watcher) error { 400 // used to stop the watch 401 stop := make(chan bool) 402 403 // manage this loop 404 go func() { 405 defer w.Stop() 406 407 select { 408 // wait for exit 409 case <-c.exit: 410 return 411 // we've been stopped 412 case <-stop: 413 return 414 } 415 }() 416 417 for { 418 res, err := w.Next() 419 if err != nil { 420 close(stop) 421 return err 422 } 423 424 // reset the error status since we succeeded 425 if err := c.getStatus(); err != nil { 426 // reset status 427 c.setStatus(nil) 428 } 429 430 c.update(res) 431 } 432 } 433 434 func (c *cache) GetService(service string, opts ...registry.GetOption) ([]*registry.Service, error) { 435 // get the service 436 services, err := c.get(service) 437 if err != nil { 438 return nil, err 439 } 440 441 // if there's nothing return err 442 if len(services) == 0 { 443 return nil, registry.ErrNotFound 444 } 445 446 // return services 447 return services, nil 448 } 449 450 func (c *cache) Stop() { 451 c.Lock() 452 defer c.Unlock() 453 454 select { 455 case <-c.exit: 456 return 457 default: 458 close(c.exit) 459 } 460 } 461 462 func (c *cache) String() string { 463 return "cache" 464 } 465 466 // New returns a new cache 467 func New(r registry.Registry, opts ...Option) Cache { 468 rand.Seed(time.Now().UnixNano()) 469 options := Options{ 470 TTL: DefaultTTL, 471 } 472 473 for _, o := range opts { 474 o(&options) 475 } 476 477 return &cache{ 478 Registry: r, 479 opts: options, 480 watched: make(map[string]bool), 481 cache: make(map[string][]*registry.Service), 482 ttls: make(map[string]time.Time), 483 exit: make(chan bool), 484 } 485 }