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

     1  package discov
     2  
     3  import (
     4  	"sync"
     5  	"sync/atomic"
     6  
     7  	"github.com/lingyao2333/mo-zero/core/discov/internal"
     8  	"github.com/lingyao2333/mo-zero/core/logx"
     9  	"github.com/lingyao2333/mo-zero/core/syncx"
    10  )
    11  
    12  type (
    13  	// SubOption defines the method to customize a Subscriber.
    14  	SubOption func(sub *Subscriber)
    15  
    16  	// A Subscriber is used to subscribe the given key on an etcd cluster.
    17  	Subscriber struct {
    18  		endpoints []string
    19  		exclusive bool
    20  		items     *container
    21  	}
    22  )
    23  
    24  // NewSubscriber returns a Subscriber.
    25  // endpoints is the hosts of the etcd cluster.
    26  // key is the key to subscribe.
    27  // opts are used to customize the Subscriber.
    28  func NewSubscriber(endpoints []string, key string, opts ...SubOption) (*Subscriber, error) {
    29  	sub := &Subscriber{
    30  		endpoints: endpoints,
    31  	}
    32  	for _, opt := range opts {
    33  		opt(sub)
    34  	}
    35  	sub.items = newContainer(sub.exclusive)
    36  
    37  	if err := internal.GetRegistry().Monitor(endpoints, key, sub.items); err != nil {
    38  		return nil, err
    39  	}
    40  
    41  	return sub, nil
    42  }
    43  
    44  // AddListener adds listener to s.
    45  func (s *Subscriber) AddListener(listener func()) {
    46  	s.items.addListener(listener)
    47  }
    48  
    49  // Values returns all the subscription values.
    50  func (s *Subscriber) Values() []string {
    51  	return s.items.getValues()
    52  }
    53  
    54  // Exclusive means that key value can only be 1:1,
    55  // which means later added value will remove the keys associated with the same value previously.
    56  func Exclusive() SubOption {
    57  	return func(sub *Subscriber) {
    58  		sub.exclusive = true
    59  	}
    60  }
    61  
    62  // WithSubEtcdAccount provides the etcd username/password.
    63  func WithSubEtcdAccount(user, pass string) SubOption {
    64  	return func(sub *Subscriber) {
    65  		RegisterAccount(sub.endpoints, user, pass)
    66  	}
    67  }
    68  
    69  // WithSubEtcdTLS provides the etcd CertFile/CertKeyFile/CACertFile.
    70  func WithSubEtcdTLS(certFile, certKeyFile, caFile string, insecureSkipVerify bool) SubOption {
    71  	return func(sub *Subscriber) {
    72  		logx.Must(RegisterTLS(sub.endpoints, certFile, certKeyFile, caFile, insecureSkipVerify))
    73  	}
    74  }
    75  
    76  type container struct {
    77  	exclusive bool
    78  	values    map[string][]string
    79  	mapping   map[string]string
    80  	snapshot  atomic.Value
    81  	dirty     *syncx.AtomicBool
    82  	listeners []func()
    83  	lock      sync.Mutex
    84  }
    85  
    86  func newContainer(exclusive bool) *container {
    87  	return &container{
    88  		exclusive: exclusive,
    89  		values:    make(map[string][]string),
    90  		mapping:   make(map[string]string),
    91  		dirty:     syncx.ForAtomicBool(true),
    92  	}
    93  }
    94  
    95  func (c *container) OnAdd(kv internal.KV) {
    96  	c.addKv(kv.Key, kv.Val)
    97  	c.notifyChange()
    98  }
    99  
   100  func (c *container) OnDelete(kv internal.KV) {
   101  	c.removeKey(kv.Key)
   102  	c.notifyChange()
   103  }
   104  
   105  // addKv adds the kv, returns if there are already other keys associate with the value
   106  func (c *container) addKv(key, value string) ([]string, bool) {
   107  	c.lock.Lock()
   108  	defer c.lock.Unlock()
   109  
   110  	c.dirty.Set(true)
   111  	keys := c.values[value]
   112  	previous := append([]string(nil), keys...)
   113  	early := len(keys) > 0
   114  	if c.exclusive && early {
   115  		for _, each := range keys {
   116  			c.doRemoveKey(each)
   117  		}
   118  	}
   119  	c.values[value] = append(c.values[value], key)
   120  	c.mapping[key] = value
   121  
   122  	if early {
   123  		return previous, true
   124  	}
   125  
   126  	return nil, false
   127  }
   128  
   129  func (c *container) addListener(listener func()) {
   130  	c.lock.Lock()
   131  	c.listeners = append(c.listeners, listener)
   132  	c.lock.Unlock()
   133  }
   134  
   135  func (c *container) doRemoveKey(key string) {
   136  	server, ok := c.mapping[key]
   137  	if !ok {
   138  		return
   139  	}
   140  
   141  	delete(c.mapping, key)
   142  	keys := c.values[server]
   143  	remain := keys[:0]
   144  
   145  	for _, k := range keys {
   146  		if k != key {
   147  			remain = append(remain, k)
   148  		}
   149  	}
   150  
   151  	if len(remain) > 0 {
   152  		c.values[server] = remain
   153  	} else {
   154  		delete(c.values, server)
   155  	}
   156  }
   157  
   158  func (c *container) getValues() []string {
   159  	if !c.dirty.True() {
   160  		return c.snapshot.Load().([]string)
   161  	}
   162  
   163  	c.lock.Lock()
   164  	defer c.lock.Unlock()
   165  
   166  	var vals []string
   167  	for each := range c.values {
   168  		vals = append(vals, each)
   169  	}
   170  	c.snapshot.Store(vals)
   171  	c.dirty.Set(false)
   172  
   173  	return vals
   174  }
   175  
   176  func (c *container) notifyChange() {
   177  	c.lock.Lock()
   178  	listeners := append(([]func())(nil), c.listeners...)
   179  	c.lock.Unlock()
   180  
   181  	for _, listener := range listeners {
   182  		listener()
   183  	}
   184  }
   185  
   186  // removeKey removes the kv, returns true if there are still other keys associate with the value
   187  func (c *container) removeKey(key string) {
   188  	c.lock.Lock()
   189  	defer c.lock.Unlock()
   190  
   191  	c.dirty.Set(true)
   192  	c.doRemoveKey(key)
   193  }