dubbo.apache.org/dubbo-go/v3@v3.1.1/remoting/etcdv3/listener.go (about) 1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package etcdv3 19 20 import ( 21 "sync" 22 "time" 23 ) 24 25 import ( 26 gxetcd "github.com/dubbogo/gost/database/kv/etcd/v3" 27 "github.com/dubbogo/gost/log/logger" 28 29 perrors "github.com/pkg/errors" 30 31 "go.etcd.io/etcd/api/v3/mvccpb" 32 33 clientv3 "go.etcd.io/etcd/client/v3" 34 ) 35 36 import ( 37 "dubbo.apache.org/dubbo-go/v3/remoting" 38 ) 39 40 // nolint 41 type EventListener struct { 42 client *gxetcd.Client 43 keyMapLock sync.RWMutex 44 keyMap map[string]struct{} 45 wg sync.WaitGroup 46 } 47 48 // NewEventListener returns a EventListener instance 49 func NewEventListener(client *gxetcd.Client) *EventListener { 50 return &EventListener{ 51 client: client, 52 keyMap: make(map[string]struct{}), 53 } 54 } 55 56 // ListenServiceNodeEvent Listen on a spec key.It will return true when spec key deleted, 57 // and return false when deep layer connection lose 58 func (l *EventListener) ListenServiceNodeEvent(key string, listener ...remoting.DataListener) bool { 59 defer l.wg.Done() 60 for { 61 wc, err := l.client.Watch(key) 62 if err != nil { 63 logger.Warnf("WatchExist{key:%s} = error{%v}", key, err) 64 return false 65 } 66 67 select { 68 69 // client stopped 70 case <-l.client.Done(): 71 logger.Warnf("etcd client stopped") 72 return false 73 74 // client ctx stop 75 case <-l.client.GetCtx().Done(): 76 logger.Warnf("etcd client ctx cancel") 77 return false 78 79 // handle etcd events 80 case e, ok := <-wc: 81 if !ok { 82 logger.Warnf("etcd watch-chan closed") 83 return false 84 } 85 86 if e.Err() != nil { 87 logger.Errorf("etcd watch ERR {err: %s}", e.Err()) 88 continue 89 } 90 for _, event := range e.Events { 91 if l.handleEvents(event, listener...) { 92 // if event is delete 93 return true 94 } 95 } 96 } 97 } 98 } 99 100 // return true means the event type is DELETE 101 // return false means the event type is CREATE || UPDATE 102 func (l *EventListener) handleEvents(event *clientv3.Event, listeners ...remoting.DataListener) bool { 103 logger.Infof("got a etcd event {type: %s, key: %s}", event.Type, event.Kv.Key) 104 105 switch event.Type { 106 // the etcdv3 event just include PUT && DELETE 107 case mvccpb.PUT: 108 for _, listener := range listeners { 109 switch event.IsCreate() { 110 case true: 111 logger.Infof("etcd get event (key{%s}) = event{EventNodeDataCreated}", event.Kv.Key) 112 listener.DataChange(remoting.Event{ 113 Path: string(event.Kv.Key), 114 Action: remoting.EventTypeAdd, 115 Content: string(event.Kv.Value), 116 }) 117 case false: 118 logger.Infof("etcd get event (key{%s}) = event{EventNodeDataChanged}", event.Kv.Key) 119 listener.DataChange(remoting.Event{ 120 Path: string(event.Kv.Key), 121 Action: remoting.EventTypeUpdate, 122 Content: string(event.Kv.Value), 123 }) 124 } 125 } 126 return false 127 case mvccpb.DELETE: 128 logger.Warnf("etcd get event (key{%s}) = event{EventNodeDeleted}", event.Kv.Key) 129 return true 130 131 default: 132 return false 133 } 134 } 135 136 // ListenServiceNodeEventWithPrefix listens on a set of key with spec prefix 137 func (l *EventListener) ListenServiceNodeEventWithPrefix(prefix string, listener ...remoting.DataListener) { 138 defer l.wg.Done() 139 for { 140 wc, err := l.client.WatchWithPrefix(prefix) 141 if err != nil { 142 logger.Warnf("listenDirEvent(key{%s}) = error{%v}", prefix, err) 143 } 144 145 select { 146 147 // client stopped 148 case <-l.client.Done(): 149 logger.Warnf("etcd client stopped") 150 return 151 152 // client ctx stop 153 case <-l.client.GetCtx().Done(): 154 logger.Warnf("etcd client ctx cancel") 155 return 156 157 // etcd event stream 158 case e, ok := <-wc: 159 160 if !ok { 161 logger.Warnf("etcd watch-chan closed") 162 return 163 } 164 165 if e.Err() != nil { 166 logger.Errorf("etcd watch ERR {err: %s}", e.Err()) 167 continue 168 } 169 for _, event := range e.Events { 170 l.handleEvents(event, listener...) 171 } 172 } 173 } 174 } 175 176 func timeSecondDuration(sec int) time.Duration { 177 return time.Duration(sec) * time.Second 178 } 179 180 // ListenServiceEvent is invoked by etcdv3 ConsumerRegistry::Registe/ etcdv3 ConsumerRegistry::get/etcdv3 ConsumerRegistry::getListener 181 // registry.go:Listen -> listenServiceEvent -> listenDirEvent -> listenServiceNodeEvent 182 // registry.go:Listen -> listenServiceEvent -> listenServiceNodeEvent 183 func (l *EventListener) ListenServiceEvent(key string, listener remoting.DataListener) { 184 l.keyMapLock.RLock() 185 _, ok := l.keyMap[key] 186 l.keyMapLock.RUnlock() 187 if ok { 188 logger.Warnf("etcdv3 key %s has already been listened.", key) 189 return 190 } 191 192 l.keyMapLock.Lock() 193 l.keyMap[key] = struct{}{} 194 l.keyMapLock.Unlock() 195 196 keyList, valueList, err := l.client.GetChildren(key) 197 if err != nil { 198 logger.Warnf("Get new node path {%v} 's content error,message is {%v}", key, perrors.WithMessage(err, "get children")) 199 } 200 201 logger.Debugf("get key children list %s, keys %v values %v", key, keyList, valueList) 202 203 for i, k := range keyList { 204 logger.Infof("got children list key -> %s", k) 205 listener.DataChange(remoting.Event{ 206 Path: k, 207 Action: remoting.EventTypeAdd, 208 Content: valueList[i], 209 }) 210 } 211 212 logger.Debugf("[ETCD Listener] listen dubbo provider key{%s} event and wait to get all provider etcdv3 nodes", key) 213 l.wg.Add(1) 214 go func(key string, listener remoting.DataListener) { 215 l.ListenServiceNodeEventWithPrefix(key, listener) 216 logger.Warnf("listenDirEvent(key{%s}) goroutine exit now", key) 217 }(key, listener) 218 219 logger.Infof("[ETCD Listener] listen dubbo service key{%s}", key) 220 l.wg.Add(1) 221 go func(key string) { 222 if l.ListenServiceNodeEvent(key) { 223 listener.DataChange(remoting.Event{Path: key, Action: remoting.EventTypeDel}) 224 } 225 logger.Warnf("listenSelf(etcd key{%s}) goroutine exit now", key) 226 }(key) 227 } 228 229 // nolint 230 func (l *EventListener) Close() { 231 l.wg.Wait() 232 }