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  }