dubbo.apache.org/dubbo-go/v3@v3.1.1/remoting/zookeeper/curator_discovery/service_discovery.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 curator_discovery
    19  
    20  import (
    21  	"encoding/json"
    22  	"path"
    23  	"strings"
    24  	"sync"
    25  )
    26  
    27  import (
    28  	"github.com/dubbogo/go-zookeeper/zk"
    29  
    30  	gxzookeeper "github.com/dubbogo/gost/database/kv/zk"
    31  	"github.com/dubbogo/gost/log/logger"
    32  
    33  	perrors "github.com/pkg/errors"
    34  )
    35  
    36  import (
    37  	"dubbo.apache.org/dubbo-go/v3/common/constant"
    38  	"dubbo.apache.org/dubbo-go/v3/remoting"
    39  	"dubbo.apache.org/dubbo-go/v3/remoting/zookeeper"
    40  )
    41  
    42  // Entry contain a service instance
    43  type Entry struct {
    44  	sync.Mutex
    45  	instance *ServiceInstance
    46  }
    47  
    48  // ServiceDiscovery which define in curator-x-discovery, please refer to
    49  // https://github.com/apache/curator/blob/master/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/ServiceDiscovery.java
    50  // It's not exactly the same as curator-x-discovery's service discovery
    51  type ServiceDiscovery struct {
    52  	client   *gxzookeeper.ZookeeperClient
    53  	mutex    *sync.Mutex
    54  	basePath string
    55  	services *sync.Map
    56  	listener *zookeeper.ZkEventListener
    57  }
    58  
    59  // NewServiceDiscovery the constructor of service discovery
    60  func NewServiceDiscovery(client *gxzookeeper.ZookeeperClient, basePath string) *ServiceDiscovery {
    61  	return &ServiceDiscovery{
    62  		client:   client,
    63  		mutex:    &sync.Mutex{},
    64  		basePath: basePath,
    65  		services: &sync.Map{},
    66  		listener: zookeeper.NewZkEventListener(client),
    67  	}
    68  }
    69  
    70  // registerService register service to zookeeper
    71  func (sd *ServiceDiscovery) registerService(instance *ServiceInstance) error {
    72  	path := sd.pathForInstance(instance.Name, instance.ID)
    73  	data, err := json.Marshal(instance)
    74  	if err != nil {
    75  		return err
    76  	}
    77  
    78  	err = sd.client.Delete(path)
    79  	if err != nil {
    80  		logger.Infof("Failed when trying to delete node %s, will continue with the registration process. "+
    81  			"This is designed to avoid previous ephemeral node hold the position,"+
    82  			" so it's normal for this action to fail because the node might not exist or has been deleted, error msg is %s.", path, err.Error())
    83  	}
    84  
    85  	err = sd.client.CreateTempWithValue(path, data)
    86  	if err == zk.ErrNodeExists {
    87  		_, state, _ := sd.client.GetContent(path)
    88  		if state != nil {
    89  			_, err = sd.client.SetContent(path, data, state.Version+1)
    90  			if err != nil {
    91  				logger.Debugf("Try to update the node data failed. In most cases, it's not a problem. ")
    92  			}
    93  		}
    94  		return nil
    95  	}
    96  	if err != nil {
    97  		return err
    98  	}
    99  	return nil
   100  }
   101  
   102  // RegisterService register service to zookeeper, and ensure cache is consistent with zookeeper
   103  func (sd *ServiceDiscovery) RegisterService(instance *ServiceInstance) error {
   104  	value, loaded := sd.services.LoadOrStore(instance.ID, &Entry{})
   105  	entry, ok := value.(*Entry)
   106  	if !ok {
   107  		return perrors.New("[ServiceDiscovery] services value not entry")
   108  	}
   109  	entry.Lock()
   110  	defer entry.Unlock()
   111  	entry.instance = instance
   112  	err := sd.registerService(instance)
   113  	if err != nil {
   114  		return err
   115  	}
   116  	if !loaded {
   117  		sd.ListenServiceInstanceEvent(instance.Name, instance.ID, sd)
   118  	}
   119  	return nil
   120  }
   121  
   122  // UpdateService update service in zookeeper, and ensure cache is consistent with zookeeper
   123  func (sd *ServiceDiscovery) UpdateService(instance *ServiceInstance) error {
   124  	value, ok := sd.services.Load(instance.ID)
   125  	if !ok {
   126  		return perrors.Errorf("[ServiceDiscovery] Service{%s} not registered", instance.ID)
   127  	}
   128  	entry, ok := value.(*Entry)
   129  	if !ok {
   130  		return perrors.New("[ServiceDiscovery] services value not entry")
   131  	}
   132  	data, err := json.Marshal(instance)
   133  	if err != nil {
   134  		return err
   135  	}
   136  
   137  	entry.Lock()
   138  	defer entry.Unlock()
   139  	entry.instance = instance
   140  	path := sd.pathForInstance(instance.Name, instance.ID)
   141  
   142  	_, err = sd.client.SetContent(path, data, -1)
   143  	if err != nil {
   144  		return err
   145  	}
   146  	return nil
   147  }
   148  
   149  // updateInternalService update service in cache
   150  func (sd *ServiceDiscovery) updateInternalService(name, id string) {
   151  	value, ok := sd.services.Load(id)
   152  	if !ok {
   153  		return
   154  	}
   155  	entry, ok := value.(*Entry)
   156  	if !ok {
   157  		return
   158  	}
   159  	entry.Lock()
   160  	defer entry.Unlock()
   161  	instance, err := sd.QueryForInstance(name, id)
   162  	if err != nil {
   163  		logger.Infof("[zkServiceDiscovery] UpdateInternalService{%s} error = err{%v}", id, err)
   164  		return
   165  	}
   166  	entry.instance = instance
   167  }
   168  
   169  // UnregisterService un-register service in zookeeper and delete service in cache
   170  func (sd *ServiceDiscovery) UnregisterService(instance *ServiceInstance) error {
   171  	_, ok := sd.services.Load(instance.ID)
   172  	if !ok {
   173  		return nil
   174  	}
   175  	sd.services.Delete(instance.ID)
   176  	return sd.unregisterService(instance)
   177  }
   178  
   179  // unregisterService un-register service in zookeeper
   180  func (sd *ServiceDiscovery) unregisterService(instance *ServiceInstance) error {
   181  	path := sd.pathForInstance(instance.Name, instance.ID)
   182  	return sd.client.Delete(path)
   183  }
   184  
   185  // ReRegisterServices re-register all cache services to zookeeper
   186  func (sd *ServiceDiscovery) ReRegisterServices() {
   187  	sd.services.Range(func(key, value interface{}) bool {
   188  		entry, ok := value.(*Entry)
   189  		if !ok {
   190  			return true
   191  		}
   192  		entry.Lock()
   193  		defer entry.Unlock()
   194  		instance := entry.instance
   195  		err := sd.registerService(instance)
   196  		if err != nil {
   197  			logger.Errorf("[zkServiceDiscovery] registerService{%s} error = err{%v}", instance.ID, perrors.WithStack(err))
   198  			return true
   199  		}
   200  		sd.ListenServiceInstanceEvent(instance.Name, instance.ID, sd)
   201  		return true
   202  	})
   203  }
   204  
   205  // QueryForInstances query instances in zookeeper by name
   206  func (sd *ServiceDiscovery) QueryForInstances(name string) ([]*ServiceInstance, error) {
   207  	ids, err := sd.client.GetChildren(sd.pathForName(name))
   208  	if err != nil {
   209  		return nil, err
   210  	}
   211  	var (
   212  		instance  *ServiceInstance
   213  		instances []*ServiceInstance
   214  	)
   215  	for _, id := range ids {
   216  		instance, err = sd.QueryForInstance(name, id)
   217  		if err != nil {
   218  			return nil, err
   219  		}
   220  		instances = append(instances, instance)
   221  	}
   222  	return instances, nil
   223  }
   224  
   225  // QueryForInstance query instances in zookeeper by name and id
   226  func (sd *ServiceDiscovery) QueryForInstance(name string, id string) (*ServiceInstance, error) {
   227  	path := sd.pathForInstance(name, id)
   228  	data, _, err := sd.client.GetContent(path)
   229  	if err != nil {
   230  		return nil, err
   231  	}
   232  	instance := &ServiceInstance{}
   233  	err = json.Unmarshal(data, instance)
   234  	if err != nil {
   235  		return nil, err
   236  	}
   237  	return instance, nil
   238  }
   239  
   240  // QueryForNames query all service name in zookeeper
   241  func (sd *ServiceDiscovery) QueryForNames() ([]string, error) {
   242  	return sd.client.GetChildren(sd.basePath)
   243  }
   244  
   245  // ListenServiceEvent add a listener in a service
   246  func (sd *ServiceDiscovery) ListenServiceEvent(name string, listener remoting.DataListener) {
   247  	sd.listener.ListenServiceEvent(nil, sd.pathForName(name), listener)
   248  }
   249  
   250  // ListenServiceInstanceEvent add a listener in an instance
   251  func (sd *ServiceDiscovery) ListenServiceInstanceEvent(name, id string, listener remoting.DataListener) {
   252  	sd.listener.ListenServiceNodeEvent(sd.pathForInstance(name, id), listener)
   253  }
   254  
   255  // DataChange implement DataListener's DataChange function
   256  func (sd *ServiceDiscovery) DataChange(eventType remoting.Event) bool {
   257  	path := eventType.Path
   258  	name, id, err := sd.getNameAndID(path)
   259  	if err != nil {
   260  		logger.Errorf("[ServiceDiscovery] data change error = {%v}", err)
   261  		return true
   262  	}
   263  	sd.updateInternalService(name, id)
   264  	return true
   265  }
   266  
   267  // getNameAndID get service name and instance id by path
   268  func (sd *ServiceDiscovery) getNameAndID(path string) (string, string, error) {
   269  	path = strings.TrimPrefix(path, sd.basePath)
   270  	path = strings.TrimPrefix(path, constant.PathSeparator)
   271  	pathSlice := strings.Split(path, constant.PathSeparator)
   272  	if len(pathSlice) < 2 {
   273  		return "", "", perrors.Errorf("[ServiceDiscovery] path{%s} dont contain name and id", path)
   274  	}
   275  	name := pathSlice[0]
   276  	id := pathSlice[1]
   277  	return name, id, nil
   278  }
   279  
   280  // nolint
   281  func (sd *ServiceDiscovery) pathForInstance(name, id string) string {
   282  	return path.Join(sd.basePath, name, id)
   283  }
   284  
   285  // nolint
   286  func (sd *ServiceDiscovery) prefixPathForInstance(name string) string {
   287  	return path.Join(sd.basePath, name)
   288  }
   289  
   290  // nolint
   291  func (sd *ServiceDiscovery) pathForName(name string) string {
   292  	return path.Join(sd.basePath, name)
   293  }
   294  
   295  func (sd *ServiceDiscovery) Close() {
   296  	if sd.listener != nil {
   297  		sd.listener.Close()
   298  	}
   299  	if sd.client != nil {
   300  		sd.client.Close()
   301  	}
   302  }