dubbo.apache.org/dubbo-go/v3@v3.1.1/registry/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  	"strings"
    22  	"sync"
    23  )
    24  
    25  import (
    26  	gxchan "github.com/dubbogo/gost/container/chan"
    27  	"github.com/dubbogo/gost/log/logger"
    28  
    29  	perrors "github.com/pkg/errors"
    30  )
    31  
    32  import (
    33  	"dubbo.apache.org/dubbo-go/v3/common"
    34  	"dubbo.apache.org/dubbo-go/v3/config_center"
    35  	"dubbo.apache.org/dubbo-go/v3/registry"
    36  	"dubbo.apache.org/dubbo-go/v3/remoting"
    37  )
    38  
    39  type dataListener struct {
    40  	interestedURL []*common.URL
    41  	listener      config_center.ConfigurationListener
    42  }
    43  
    44  // NewRegistryDataListener creates a data listener for etcd
    45  func NewRegistryDataListener(listener config_center.ConfigurationListener) *dataListener {
    46  	return &dataListener{listener: listener}
    47  }
    48  
    49  // AddInterestedURL adds a registration @url to listen
    50  func (l *dataListener) AddInterestedURL(url *common.URL) {
    51  	l.interestedURL = append(l.interestedURL, url)
    52  }
    53  
    54  // DataChange processes the data change event from registry center of etcd
    55  func (l *dataListener) DataChange(eventType remoting.Event) bool {
    56  	index := strings.Index(eventType.Path, "/providers/")
    57  	if index == -1 {
    58  		logger.Warnf("Listen with no url, event.path={%v}", eventType.Path)
    59  		return false
    60  	}
    61  	url := eventType.Path[index+len("/providers/"):]
    62  	serviceURL, err := common.NewURL(url)
    63  	if err != nil {
    64  		logger.Warnf("Listen NewURL(r{%s}) = error{%v}", eventType.Path, err)
    65  		return false
    66  	}
    67  
    68  	for _, v := range l.interestedURL {
    69  		if serviceURL.URLEqual(v) {
    70  			l.listener.Process(
    71  				&config_center.ConfigChangeEvent{
    72  					Key:        eventType.Path,
    73  					Value:      serviceURL,
    74  					ConfigType: eventType.Action,
    75  				},
    76  			)
    77  			return true
    78  		}
    79  	}
    80  	return false
    81  }
    82  
    83  type configurationListener struct {
    84  	registry  *etcdV3Registry
    85  	events    *gxchan.UnboundedChan
    86  	closeOnce sync.Once
    87  }
    88  
    89  // NewConfigurationListener for listening the event of etcdv3.
    90  func NewConfigurationListener(reg *etcdV3Registry) *configurationListener {
    91  	// add a new waiter
    92  	reg.WaitGroup().Add(1)
    93  	return &configurationListener{registry: reg, events: gxchan.NewUnboundedChan(32)}
    94  }
    95  
    96  // Process data change event from config center of etcd
    97  func (l *configurationListener) Process(configType *config_center.ConfigChangeEvent) {
    98  	l.events.In() <- configType
    99  }
   100  
   101  // Next returns next service event once received
   102  func (l *configurationListener) Next() (*registry.ServiceEvent, error) {
   103  	for {
   104  		select {
   105  		case <-l.registry.Done():
   106  			logger.Warnf("listener's etcd client connection is broken, so etcd event listener exit now.")
   107  			return nil, perrors.New("listener stopped")
   108  
   109  		case val := <-l.events.Out():
   110  			e, _ := val.(*config_center.ConfigChangeEvent)
   111  			logger.Infof("got etcd event %#v", e)
   112  			if e.ConfigType == remoting.EventTypeDel && l.registry.client.Valid() {
   113  				select {
   114  				case <-l.registry.Done():
   115  					logger.Warnf("update @result{%s}. But its connection to registry is invalid", e.Value)
   116  				default:
   117  				}
   118  				continue
   119  			}
   120  			return &registry.ServiceEvent{Action: e.ConfigType, Service: e.Value.(*common.URL)}, nil
   121  		}
   122  	}
   123  }
   124  
   125  // Close etcd registry center
   126  func (l *configurationListener) Close() {
   127  	l.closeOnce.Do(func() {
   128  		l.registry.WaitGroup().Done()
   129  	})
   130  }