github.com/lingyao2333/mo-zero@v1.4.1/core/discov/internal/registry.go (about)

     1  package internal
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io"
     7  	"sort"
     8  	"strings"
     9  	"sync"
    10  	"time"
    11  
    12  	"github.com/lingyao2333/mo-zero/core/contextx"
    13  	"github.com/lingyao2333/mo-zero/core/lang"
    14  	"github.com/lingyao2333/mo-zero/core/logx"
    15  	"github.com/lingyao2333/mo-zero/core/syncx"
    16  	"github.com/lingyao2333/mo-zero/core/threading"
    17  	clientv3 "go.etcd.io/etcd/client/v3"
    18  )
    19  
    20  var (
    21  	registry = Registry{
    22  		clusters: make(map[string]*cluster),
    23  	}
    24  	connManager = syncx.NewResourceManager()
    25  )
    26  
    27  // A Registry is a registry that manages the etcd client connections.
    28  type Registry struct {
    29  	clusters map[string]*cluster
    30  	lock     sync.Mutex
    31  }
    32  
    33  // GetRegistry returns a global Registry.
    34  func GetRegistry() *Registry {
    35  	return &registry
    36  }
    37  
    38  // GetConn returns an etcd client connection associated with given endpoints.
    39  func (r *Registry) GetConn(endpoints []string) (EtcdClient, error) {
    40  	c, _ := r.getCluster(endpoints)
    41  	return c.getClient()
    42  }
    43  
    44  // Monitor monitors the key on given etcd endpoints, notify with the given UpdateListener.
    45  func (r *Registry) Monitor(endpoints []string, key string, l UpdateListener) error {
    46  	c, exists := r.getCluster(endpoints)
    47  	// if exists, the existing values should be updated to the listener.
    48  	if exists {
    49  		kvs := c.getCurrent(key)
    50  		for _, kv := range kvs {
    51  			l.OnAdd(kv)
    52  		}
    53  	}
    54  
    55  	return c.monitor(key, l)
    56  }
    57  
    58  func (r *Registry) getCluster(endpoints []string) (c *cluster, exists bool) {
    59  	clusterKey := getClusterKey(endpoints)
    60  	r.lock.Lock()
    61  	defer r.lock.Unlock()
    62  	c, exists = r.clusters[clusterKey]
    63  	if !exists {
    64  		c = newCluster(endpoints)
    65  		r.clusters[clusterKey] = c
    66  	}
    67  
    68  	return
    69  }
    70  
    71  type cluster struct {
    72  	endpoints  []string
    73  	key        string
    74  	values     map[string]map[string]string
    75  	listeners  map[string][]UpdateListener
    76  	watchGroup *threading.RoutineGroup
    77  	done       chan lang.PlaceholderType
    78  	lock       sync.Mutex
    79  }
    80  
    81  func newCluster(endpoints []string) *cluster {
    82  	return &cluster{
    83  		endpoints:  endpoints,
    84  		key:        getClusterKey(endpoints),
    85  		values:     make(map[string]map[string]string),
    86  		listeners:  make(map[string][]UpdateListener),
    87  		watchGroup: threading.NewRoutineGroup(),
    88  		done:       make(chan lang.PlaceholderType),
    89  	}
    90  }
    91  
    92  func (c *cluster) context(cli EtcdClient) context.Context {
    93  	return contextx.ValueOnlyFrom(cli.Ctx())
    94  }
    95  
    96  func (c *cluster) getClient() (EtcdClient, error) {
    97  	val, err := connManager.GetResource(c.key, func() (io.Closer, error) {
    98  		return c.newClient()
    99  	})
   100  	if err != nil {
   101  		return nil, err
   102  	}
   103  
   104  	return val.(EtcdClient), nil
   105  }
   106  
   107  func (c *cluster) getCurrent(key string) []KV {
   108  	c.lock.Lock()
   109  	defer c.lock.Unlock()
   110  
   111  	var kvs []KV
   112  	for k, v := range c.values[key] {
   113  		kvs = append(kvs, KV{
   114  			Key: k,
   115  			Val: v,
   116  		})
   117  	}
   118  
   119  	return kvs
   120  }
   121  
   122  func (c *cluster) handleChanges(key string, kvs []KV) {
   123  	var add []KV
   124  	var remove []KV
   125  	c.lock.Lock()
   126  	listeners := append([]UpdateListener(nil), c.listeners[key]...)
   127  	vals, ok := c.values[key]
   128  	if !ok {
   129  		add = kvs
   130  		vals = make(map[string]string)
   131  		for _, kv := range kvs {
   132  			vals[kv.Key] = kv.Val
   133  		}
   134  		c.values[key] = vals
   135  	} else {
   136  		m := make(map[string]string)
   137  		for _, kv := range kvs {
   138  			m[kv.Key] = kv.Val
   139  		}
   140  		for k, v := range vals {
   141  			if val, ok := m[k]; !ok || v != val {
   142  				remove = append(remove, KV{
   143  					Key: k,
   144  					Val: v,
   145  				})
   146  			}
   147  		}
   148  		for k, v := range m {
   149  			if val, ok := vals[k]; !ok || v != val {
   150  				add = append(add, KV{
   151  					Key: k,
   152  					Val: v,
   153  				})
   154  			}
   155  		}
   156  		c.values[key] = m
   157  	}
   158  	c.lock.Unlock()
   159  
   160  	for _, kv := range add {
   161  		for _, l := range listeners {
   162  			l.OnAdd(kv)
   163  		}
   164  	}
   165  	for _, kv := range remove {
   166  		for _, l := range listeners {
   167  			l.OnDelete(kv)
   168  		}
   169  	}
   170  }
   171  
   172  func (c *cluster) handleWatchEvents(key string, events []*clientv3.Event) {
   173  	c.lock.Lock()
   174  	listeners := append([]UpdateListener(nil), c.listeners[key]...)
   175  	c.lock.Unlock()
   176  
   177  	for _, ev := range events {
   178  		switch ev.Type {
   179  		case clientv3.EventTypePut:
   180  			c.lock.Lock()
   181  			if vals, ok := c.values[key]; ok {
   182  				vals[string(ev.Kv.Key)] = string(ev.Kv.Value)
   183  			} else {
   184  				c.values[key] = map[string]string{string(ev.Kv.Key): string(ev.Kv.Value)}
   185  			}
   186  			c.lock.Unlock()
   187  			for _, l := range listeners {
   188  				l.OnAdd(KV{
   189  					Key: string(ev.Kv.Key),
   190  					Val: string(ev.Kv.Value),
   191  				})
   192  			}
   193  		case clientv3.EventTypeDelete:
   194  			c.lock.Lock()
   195  			if vals, ok := c.values[key]; ok {
   196  				delete(vals, string(ev.Kv.Key))
   197  			}
   198  			c.lock.Unlock()
   199  			for _, l := range listeners {
   200  				l.OnDelete(KV{
   201  					Key: string(ev.Kv.Key),
   202  					Val: string(ev.Kv.Value),
   203  				})
   204  			}
   205  		default:
   206  			logx.Errorf("Unknown event type: %v", ev.Type)
   207  		}
   208  	}
   209  }
   210  
   211  func (c *cluster) load(cli EtcdClient, key string) int64 {
   212  	var resp *clientv3.GetResponse
   213  	for {
   214  		var err error
   215  		ctx, cancel := context.WithTimeout(c.context(cli), RequestTimeout)
   216  		resp, err = cli.Get(ctx, makeKeyPrefix(key), clientv3.WithPrefix())
   217  		cancel()
   218  		if err == nil {
   219  			break
   220  		}
   221  
   222  		logx.Error(err)
   223  		time.Sleep(coolDownInterval)
   224  	}
   225  
   226  	var kvs []KV
   227  	for _, ev := range resp.Kvs {
   228  		kvs = append(kvs, KV{
   229  			Key: string(ev.Key),
   230  			Val: string(ev.Value),
   231  		})
   232  	}
   233  
   234  	c.handleChanges(key, kvs)
   235  
   236  	return resp.Header.Revision
   237  }
   238  
   239  func (c *cluster) monitor(key string, l UpdateListener) error {
   240  	c.lock.Lock()
   241  	c.listeners[key] = append(c.listeners[key], l)
   242  	c.lock.Unlock()
   243  
   244  	cli, err := c.getClient()
   245  	if err != nil {
   246  		return err
   247  	}
   248  
   249  	rev := c.load(cli, key)
   250  	c.watchGroup.Run(func() {
   251  		c.watch(cli, key, rev)
   252  	})
   253  
   254  	return nil
   255  }
   256  
   257  func (c *cluster) newClient() (EtcdClient, error) {
   258  	cli, err := NewClient(c.endpoints)
   259  	if err != nil {
   260  		return nil, err
   261  	}
   262  
   263  	go c.watchConnState(cli)
   264  
   265  	return cli, nil
   266  }
   267  
   268  func (c *cluster) reload(cli EtcdClient) {
   269  	c.lock.Lock()
   270  	close(c.done)
   271  	c.watchGroup.Wait()
   272  	c.done = make(chan lang.PlaceholderType)
   273  	c.watchGroup = threading.NewRoutineGroup()
   274  	var keys []string
   275  	for k := range c.listeners {
   276  		keys = append(keys, k)
   277  	}
   278  	c.lock.Unlock()
   279  
   280  	for _, key := range keys {
   281  		k := key
   282  		c.watchGroup.Run(func() {
   283  			rev := c.load(cli, k)
   284  			c.watch(cli, k, rev)
   285  		})
   286  	}
   287  }
   288  
   289  func (c *cluster) watch(cli EtcdClient, key string, rev int64) {
   290  	for {
   291  		if c.watchStream(cli, key, rev) {
   292  			return
   293  		}
   294  	}
   295  }
   296  
   297  func (c *cluster) watchStream(cli EtcdClient, key string, rev int64) bool {
   298  	var rch clientv3.WatchChan
   299  	if rev != 0 {
   300  		rch = cli.Watch(clientv3.WithRequireLeader(c.context(cli)), makeKeyPrefix(key), clientv3.WithPrefix(),
   301  			clientv3.WithRev(rev+1))
   302  	} else {
   303  		rch = cli.Watch(clientv3.WithRequireLeader(c.context(cli)), makeKeyPrefix(key), clientv3.WithPrefix())
   304  	}
   305  
   306  	for {
   307  		select {
   308  		case wresp, ok := <-rch:
   309  			if !ok {
   310  				logx.Error("etcd monitor chan has been closed")
   311  				return false
   312  			}
   313  			if wresp.Canceled {
   314  				logx.Errorf("etcd monitor chan has been canceled, error: %v", wresp.Err())
   315  				return false
   316  			}
   317  			if wresp.Err() != nil {
   318  				logx.Error(fmt.Sprintf("etcd monitor chan error: %v", wresp.Err()))
   319  				return false
   320  			}
   321  
   322  			c.handleWatchEvents(key, wresp.Events)
   323  		case <-c.done:
   324  			return true
   325  		}
   326  	}
   327  }
   328  
   329  func (c *cluster) watchConnState(cli EtcdClient) {
   330  	watcher := newStateWatcher()
   331  	watcher.addListener(func() {
   332  		go c.reload(cli)
   333  	})
   334  	watcher.watch(cli.ActiveConnection())
   335  }
   336  
   337  // DialClient dials an etcd cluster with given endpoints.
   338  func DialClient(endpoints []string) (EtcdClient, error) {
   339  	cfg := clientv3.Config{
   340  		Endpoints:            endpoints,
   341  		AutoSyncInterval:     autoSyncInterval,
   342  		DialTimeout:          DialTimeout,
   343  		DialKeepAliveTime:    dialKeepAliveTime,
   344  		DialKeepAliveTimeout: DialTimeout,
   345  		RejectOldCluster:     true,
   346  		PermitWithoutStream:  true,
   347  	}
   348  	if account, ok := GetAccount(endpoints); ok {
   349  		cfg.Username = account.User
   350  		cfg.Password = account.Pass
   351  	}
   352  	if tlsCfg, ok := GetTLS(endpoints); ok {
   353  		cfg.TLS = tlsCfg
   354  	}
   355  
   356  	return clientv3.New(cfg)
   357  }
   358  
   359  func getClusterKey(endpoints []string) string {
   360  	sort.Strings(endpoints)
   361  	return strings.Join(endpoints, endpointsSeparator)
   362  }
   363  
   364  func makeKeyPrefix(key string) string {
   365  	return fmt.Sprintf("%s%c", key, Delimiter)
   366  }