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  }