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