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  }