dubbo.apache.org/dubbo-go/v3@v3.1.1/remoting/zookeeper/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 zookeeper 19 20 import ( 21 "net/url" 22 "path" 23 "strings" 24 "sync" 25 "time" 26 ) 27 28 import ( 29 "github.com/dubbogo/go-zookeeper/zk" 30 31 gxzookeeper "github.com/dubbogo/gost/database/kv/zk" 32 "github.com/dubbogo/gost/log/logger" 33 34 perrors "github.com/pkg/errors" 35 36 uatomic "go.uber.org/atomic" 37 ) 38 39 import ( 40 "dubbo.apache.org/dubbo-go/v3/common" 41 "dubbo.apache.org/dubbo-go/v3/common/constant" 42 "dubbo.apache.org/dubbo-go/v3/remoting" 43 ) 44 45 var defaultTTL = 10 * time.Minute 46 47 // nolint 48 type ZkEventListener struct { 49 Client *gxzookeeper.ZookeeperClient 50 pathMapLock sync.Mutex 51 pathMap map[string]*uatomic.Int32 52 wg sync.WaitGroup 53 exit chan struct{} 54 } 55 56 // NewZkEventListener returns a EventListener instance 57 func NewZkEventListener(client *gxzookeeper.ZookeeperClient) *ZkEventListener { 58 return &ZkEventListener{ 59 Client: client, 60 pathMap: make(map[string]*uatomic.Int32), 61 exit: make(chan struct{}), 62 } 63 } 64 65 // ListenServiceNodeEvent listen a path node event 66 func (l *ZkEventListener) ListenServiceNodeEvent(zkPath string, listener remoting.DataListener) { 67 l.wg.Add(1) 68 go func(zkPath string, listener remoting.DataListener) { 69 defer l.wg.Done() 70 if l.listenServiceNodeEvent(zkPath, listener) { 71 listener.DataChange(remoting.Event{Path: zkPath, Action: remoting.EventTypeDel}) 72 } 73 l.pathMapLock.Lock() 74 delete(l.pathMap, zkPath) 75 l.pathMapLock.Unlock() 76 logger.Warnf("ListenServiceNodeEvent->listenSelf(zk path{%s}) goroutine exit now", zkPath) 77 }(zkPath, listener) 78 } 79 80 // ListenConfigurationEvent listen a path node event 81 func (l *ZkEventListener) ListenConfigurationEvent(zkPath string, listener remoting.DataListener) { 82 l.wg.Add(1) 83 go func(zkPath string, listener remoting.DataListener) { 84 var eventChan = make(chan zk.Event, 16) 85 l.Client.RegisterEvent(zkPath, eventChan) 86 for { 87 select { 88 case event := <-eventChan: 89 logger.Infof("[ZkEventListener]Receive configuration change event:%#v", event) 90 if event.Type == zk.EventNodeChildrenChanged || event.Type == zk.EventNotWatching { 91 continue 92 } 93 // 1. Re-set watcher for the zk node 94 _, _, _, err := l.Client.Conn.ExistsW(event.Path) 95 if err != nil { 96 logger.Warnf("[ZkEventListener]Re-set watcher error, the reason is %+v", err) 97 continue 98 } 99 100 action := remoting.EventTypeAdd 101 var content string 102 if event.Type == zk.EventNodeDeleted { 103 action = remoting.EventTypeDel 104 } else { 105 // 2. Try to get new configuration value of the zk node 106 // Notice: The order of step 1 and step 2 cannot be swapped, if you get value(with timestamp t1) 107 // before re-set the watcher(with timestamp t2), and some one change the data of the zk node after 108 // t2 but before t1, you may get the old value, and the new value will not trigger the event. 109 contentBytes, _, err := l.Client.Conn.Get(event.Path) 110 if err != nil { 111 logger.Warnf("[ListenConfigurationEvent]Get config value error, the reason is %+v", err) 112 continue 113 } 114 content = string(contentBytes) 115 logger.Debugf("[ZkEventListener]Successfully get new config value: %s", string(content)) 116 } 117 118 listener.DataChange(remoting.Event{ 119 Path: event.Path, 120 Action: remoting.EventType(action), 121 Content: content, 122 }) 123 case <-l.exit: 124 return 125 } 126 } 127 128 }(zkPath, listener) 129 } 130 131 // nolint 132 func (l *ZkEventListener) listenServiceNodeEvent(zkPath string, listener ...remoting.DataListener) bool { 133 l.pathMapLock.Lock() 134 a, ok := l.pathMap[zkPath] 135 if !ok || a.Load() > 1 { 136 l.pathMapLock.Unlock() 137 return false 138 } 139 a.Inc() 140 l.pathMapLock.Unlock() 141 defer a.Dec() 142 var zkEvent zk.Event 143 for { 144 keyEventCh, err := l.Client.ExistW(zkPath) 145 if err != nil { 146 logger.Warnf("existW{key:%s} = error{%v}", zkPath, err) 147 return false 148 } 149 select { 150 case zkEvent = <-keyEventCh: 151 logger.Warnf("get a zookeeper keyEventCh{type:%s, server:%s, path:%s, state:%d-%s, err:%s}", 152 zkEvent.Type.String(), zkEvent.Server, zkEvent.Path, zkEvent.State, gxzookeeper.StateToString(zkEvent.State), zkEvent.Err) 153 switch zkEvent.Type { 154 case zk.EventNodeDataChanged: 155 logger.Warnf("zk.ExistW(key{%s}) = event{EventNodeDataChanged}", zkPath) 156 if len(listener) > 0 { 157 content, _, err := l.Client.Conn.Get(zkEvent.Path) 158 if err != nil { 159 logger.Warnf("zk.Conn.Get{key:%s} = error{%v}", zkPath, err) 160 return false 161 } 162 listener[0].DataChange(remoting.Event{Path: zkEvent.Path, Action: remoting.EventTypeUpdate, Content: string(content)}) 163 } 164 case zk.EventNodeCreated: 165 logger.Warnf("[ZkEventListener][listenServiceNodeEvent]Get a EventNodeCreated event for path {%s}", zkPath) 166 if len(listener) > 0 { 167 content, _, err := l.Client.Conn.Get(zkEvent.Path) 168 if err != nil { 169 logger.Warnf("zk.Conn.Get{key:%s} = error{%v}", zkPath, err) 170 return false 171 } 172 listener[0].DataChange(remoting.Event{Path: zkEvent.Path, Action: remoting.EventTypeAdd, Content: string(content)}) 173 } 174 case zk.EventNotWatching: 175 logger.Infof("[ZkEventListener][listenServiceNodeEvent]Get a EventNotWatching event for path {%s}", zkPath) 176 case zk.EventNodeDeleted: 177 logger.Infof("[ZkEventListener][listenServiceNodeEvent]Get a EventNodeDeleted event for path {%s}", zkPath) 178 return true 179 } 180 case <-l.exit: 181 return false 182 } 183 } 184 } 185 186 func (l *ZkEventListener) handleZkNodeEvent(zkPath string, children []string, listener remoting.DataListener) { 187 contains := func(s []string, e string) bool { 188 for _, a := range s { 189 if a == e { 190 return true 191 } 192 } 193 return false 194 } 195 newChildren, err := l.Client.GetChildren(zkPath) 196 if err != nil { 197 logger.Errorf("[ZkEventListener handleZkNodeEvent]Path{%s} child nodes changed, zk.Children() = error{%v}", zkPath, perrors.WithStack(err)) 198 return 199 } 200 // a node was added -- listen the new node 201 var ( 202 newNode string 203 ) 204 for _, n := range newChildren { 205 newNode = path.Join(zkPath, n) 206 logger.Debugf("[Zookeeper Listener] add zkNode{%s}", newNode) 207 content, _, connErr := l.Client.Conn.Get(newNode) 208 if connErr != nil { 209 logger.Errorf("Get new node path {%v} 's content error,message is {%v}", 210 newNode, perrors.WithStack(connErr)) 211 } 212 if !listener.DataChange(remoting.Event{Path: newNode, Action: remoting.EventTypeAdd, Content: string(content)}) { 213 continue 214 } 215 // listen l service node 216 l.wg.Add(1) 217 go func(node string, listener remoting.DataListener) { 218 defer l.wg.Done() 219 if l.listenServiceNodeEvent(node, listener) { 220 logger.Warnf("delete zkNode{%s}", node) 221 listener.DataChange(remoting.Event{Path: node, Action: remoting.EventTypeDel}) 222 } 223 l.pathMapLock.Lock() 224 delete(l.pathMap, zkPath) 225 l.pathMapLock.Unlock() 226 logger.Debugf("handleZkNodeEvent->listenSelf(zk path{%s}) goroutine exit now", node) 227 }(newNode, listener) 228 } 229 230 // old node was deleted 231 var oldNode string 232 for _, n := range children { 233 if contains(newChildren, n) { 234 continue 235 } 236 oldNode = path.Join(zkPath, n) 237 logger.Warnf("delete oldNode{%s}", oldNode) 238 listener.DataChange(remoting.Event{Path: oldNode, Action: remoting.EventTypeDel}) 239 } 240 } 241 242 // listenerAllDirEvents listens all services when conf.InterfaceKey = "*" 243 func (l *ZkEventListener) listenAllDirEvents(conf *common.URL, listener remoting.DataListener) { 244 var ( 245 failTimes int 246 ttl time.Duration 247 ) 248 ttl = defaultTTL 249 if conf != nil { 250 if timeout, err := time.ParseDuration(conf.GetParam(constant.RegistryTTLKey, constant.DefaultRegTTL)); err == nil { 251 ttl = timeout 252 } else { 253 logger.Warnf("[Zookeeper EventListener][listenDirEvent] Wrong configuration for registry.ttl, error=%+v, using default value %v instead", err, defaultTTL) 254 } 255 } 256 if ttl > 20e9 { 257 ttl = 20e9 258 } 259 260 rootPath := path.Join(constant.PathSeparator, constant.Dubbo) 261 for { 262 // get all interfaces 263 children, childEventCh, err := l.Client.GetChildrenW(rootPath) 264 if err != nil { 265 failTimes++ 266 if MaxFailTimes <= failTimes { 267 failTimes = MaxFailTimes 268 } 269 logger.Errorf("[Zookeeper EventListener][listenDirEvent] Get children of path {%s} with watcher failed, the error is %+v", rootPath, err) 270 // Maybe the zookeeper does not ready yet, sleep failTimes * ConnDelay senconds to wait 271 after := time.After(timeSecondDuration(failTimes * ConnDelay)) 272 select { 273 case <-after: 274 continue 275 case <-l.exit: 276 return 277 } 278 } 279 failTimes = 0 280 if len(children) == 0 { 281 logger.Warnf("[Zookeeper EventListener][listenDirEvent] Can not get any children for the path \"%s\", please check if the provider does ready.", rootPath) 282 } 283 for _, c := range children { 284 // Build the child path 285 zkRootPath := path.Join(rootPath, constant.PathSeparator, url.QueryEscape(c), constant.PathSeparator, constant.ProvidersCategory) 286 // Save the path to avoid listen repeatedly 287 l.pathMapLock.Lock() 288 if _, ok := l.pathMap[zkRootPath]; ok { 289 logger.Warnf("[Zookeeper EventListener][listenDirEvent] The child with zk path {%s} has already been listened.", zkRootPath) 290 l.pathMapLock.Unlock() 291 continue 292 } else { 293 l.pathMap[zkRootPath] = uatomic.NewInt32(0) 294 } 295 l.pathMapLock.Unlock() 296 logger.Debugf("[Zookeeper EventListener][listenDirEvent] listen dubbo interface key{%s}", zkRootPath) 297 l.wg.Add(1) 298 // listen every interface 299 go l.listenDirEvent(conf, zkRootPath, listener, c) 300 } 301 302 ticker := time.NewTicker(ttl) 303 select { 304 case <-ticker.C: 305 ticker.Stop() 306 case zkEvent := <-childEventCh: 307 logger.Debugf("Get a zookeeper childEventCh{type:%s, server:%s, path:%s, state:%d-%s, err:%v}", 308 zkEvent.Type.String(), zkEvent.Server, zkEvent.Path, zkEvent.State, gxzookeeper.StateToString(zkEvent.State), zkEvent.Err) 309 ticker.Stop() 310 case <-l.exit: 311 logger.Warnf("listen(path{%s}) goroutine exit now...", rootPath) 312 ticker.Stop() 313 return 314 } 315 } 316 } 317 318 func (l *ZkEventListener) listenDirEvent(conf *common.URL, zkRootPath string, listener remoting.DataListener, intf string) { 319 defer l.wg.Done() 320 if intf == constant.AnyValue { 321 l.listenAllDirEvents(conf, listener) 322 return 323 } 324 var ( 325 failTimes int 326 ttl time.Duration 327 ) 328 ttl = defaultTTL 329 if conf != nil { 330 timeout, err := time.ParseDuration(conf.GetParam(constant.RegistryTTLKey, constant.DefaultRegTTL)) 331 if err == nil { 332 ttl = timeout 333 } else { 334 logger.Warnf("[Zookeeper EventListener][listenDirEvent] Wrong configuration for registry.ttl, error=%+v, using default value %v instead", err, defaultTTL) 335 } 336 } 337 for { 338 // Get current children with watcher for the zkRootPath 339 children, childEventCh, err := l.Client.GetChildrenW(zkRootPath) 340 if err != nil { 341 failTimes++ 342 if MaxFailTimes <= failTimes { 343 failTimes = MaxFailTimes 344 } 345 346 if !perrors.Is(err, zk.ErrNoNode) { // ignore if node not exist 347 logger.Errorf("[Zookeeper EventListener][listenDirEvent] Get children of path {%s} with watcher failed, the error is %+v", zkRootPath, err) 348 } 349 // Maybe the provider does not ready yet, sleep failTimes * ConnDelay senconds to wait 350 after := time.After(timeSecondDuration(failTimes * ConnDelay)) 351 select { 352 case <-after: 353 continue 354 case <-l.exit: 355 return 356 } 357 } 358 failTimes = 0 359 if len(children) == 0 { 360 logger.Debugf("[Zookeeper EventListener][listenDirEvent] Can not gey any children for the path {%s}, please check if the provider does ready.", zkRootPath) 361 } 362 for _, c := range children { 363 // Only need to compare Path when subscribing to provider 364 if strings.LastIndex(zkRootPath, constant.ProviderCategory) != -1 { 365 provider, _ := common.NewURL(c) 366 if provider.Interface() != intf || !common.IsAnyCondition(constant.AnyValue, conf.Group(), conf.Version(), provider) { 367 continue 368 } 369 } 370 // Build the children path 371 zkNodePath := path.Join(zkRootPath, c) 372 // Save the path to avoid listen repeatedly 373 l.pathMapLock.Lock() 374 _, ok := l.pathMap[zkNodePath] 375 if !ok { 376 l.pathMap[zkNodePath] = uatomic.NewInt32(0) 377 } 378 l.pathMapLock.Unlock() 379 if ok { 380 logger.Warnf("[Zookeeper EventListener][listenDirEvent] The child with zk path {%s} has already been listened.", zkNodePath) 381 l.Client.RLock() 382 if l.Client.Conn == nil { 383 l.Client.RUnlock() 384 break 385 } 386 content, _, err := l.Client.Conn.Get(zkNodePath) 387 l.Client.RUnlock() 388 if err != nil { 389 logger.Errorf("[Zookeeper EventListener][listenDirEvent] Get content of the child node {%v} failed, the error is %+v", zkNodePath, perrors.WithStack(err)) 390 } 391 listener.DataChange(remoting.Event{Path: zkNodePath, Action: remoting.EventTypeAdd, Content: string(content)}) 392 continue 393 } 394 // When Zk disconnected, the Conn will be set to nil, so here need check the value of Conn 395 l.Client.RLock() 396 if l.Client.Conn == nil { 397 l.Client.RUnlock() 398 break 399 } 400 content, _, err := l.Client.Conn.Get(zkNodePath) 401 l.Client.RUnlock() 402 if err != nil { 403 logger.Errorf("[Zookeeper EventListener][listenDirEvent] Get content of the child node {%v} failed, the error is %+v", zkNodePath, perrors.WithStack(err)) 404 } 405 logger.Debugf("[Zookeeper EventListener][listenDirEvent] Get children!{%s}", zkNodePath) 406 if !listener.DataChange(remoting.Event{Path: zkNodePath, Action: remoting.EventTypeAdd, Content: string(content)}) { 407 continue 408 } 409 logger.Debugf("[Zookeeper EventListener][listenDirEvent] listen dubbo service key{%s}", zkNodePath) 410 l.wg.Add(1) 411 go func(zkPath string, listener remoting.DataListener) { 412 defer l.wg.Done() 413 if l.listenServiceNodeEvent(zkPath, listener) { 414 listener.DataChange(remoting.Event{Path: zkPath, Action: remoting.EventTypeDel}) 415 } 416 l.pathMapLock.Lock() 417 delete(l.pathMap, zkPath) 418 l.pathMapLock.Unlock() 419 logger.Warnf("listenDirEvent->listenSelf(zk path{%s}) goroutine exit now", zkPath) 420 }(zkNodePath, listener) 421 } 422 if l.startScheduleWatchTask(zkRootPath, children, ttl, listener, childEventCh) { 423 return 424 } 425 } 426 } 427 428 // startScheduleWatchTask periodically update provider information, return true when receive exit signal 429 func (l *ZkEventListener) startScheduleWatchTask( 430 zkRootPath string, children []string, ttl time.Duration, 431 listener remoting.DataListener, childEventCh <-chan zk.Event) bool { 432 tickerTTL := ttl 433 if tickerTTL > 20e9 { 434 tickerTTL = 20e9 435 } 436 ticker := time.NewTicker(tickerTTL) 437 for { 438 select { 439 case <-ticker.C: 440 l.handleZkNodeEvent(zkRootPath, children, listener) 441 if tickerTTL < ttl { 442 tickerTTL *= 2 443 if tickerTTL > ttl { 444 tickerTTL = ttl 445 } 446 ticker.Stop() 447 ticker = time.NewTicker(tickerTTL) 448 } 449 case zkEvent := <-childEventCh: 450 logger.Debugf("Get a zookeeper childEventCh{type:%s, server:%s, path:%s, state:%d-%s, err:%v}", 451 zkEvent.Type.String(), zkEvent.Server, zkEvent.Path, zkEvent.State, gxzookeeper.StateToString(zkEvent.State), zkEvent.Err) 452 ticker.Stop() 453 if zkEvent.Type == zk.EventNodeChildrenChanged { 454 l.handleZkNodeEvent(zkEvent.Path, children, listener) 455 } 456 return false 457 case <-l.exit: 458 logger.Warnf("listen(path{%s}) goroutine exit now...", zkRootPath) 459 ticker.Stop() 460 return true 461 } 462 } 463 } 464 465 func timeSecondDuration(sec int) time.Duration { 466 return time.Duration(sec) * time.Second 467 } 468 469 // ListenServiceEvent is invoked by ZkConsumerRegistry::Register/ZkConsumerRegistry::get/ZkConsumerRegistry::getListener 470 // registry.go:Listen -> listenServiceEvent -> listenDirEvent -> listenServiceNodeEvent 471 // registry.go:Listen -> listenServiceEvent -> listenServiceNodeEvent 472 func (l *ZkEventListener) ListenServiceEvent(conf *common.URL, zkPath string, listener remoting.DataListener) { 473 logger.Infof("[Zookeeper Listener] listen dubbo path{%s}", zkPath) 474 l.wg.Add(1) 475 go func(zkPath string, listener remoting.DataListener) { 476 intf := "" 477 if conf != nil { 478 intf = conf.Interface() 479 } 480 l.listenDirEvent(conf, zkPath, listener, intf) 481 logger.Warnf("ListenServiceEvent->listenDirEvent(zkPath{%s}) goroutine exit now", zkPath) 482 }(zkPath, listener) 483 } 484 485 // Close will let client listen exit 486 func (l *ZkEventListener) Close() { 487 close(l.exit) 488 l.wg.Wait() 489 }