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 ®istry 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 }