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