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 }