dubbo.apache.org/dubbo-go/v3@v3.1.1/registry/zookeeper/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 zookeeper
    19  
    20  import (
    21  	"fmt"
    22  	"strconv"
    23  	"strings"
    24  	"sync"
    25  )
    26  
    27  import (
    28  	gxset "github.com/dubbogo/gost/container/set"
    29  	gxzookeeper "github.com/dubbogo/gost/database/kv/zk"
    30  	gxpage "github.com/dubbogo/gost/hash/page"
    31  	"github.com/dubbogo/gost/log/logger"
    32  )
    33  
    34  import (
    35  	"dubbo.apache.org/dubbo-go/v3/common"
    36  	"dubbo.apache.org/dubbo-go/v3/common/constant"
    37  	"dubbo.apache.org/dubbo-go/v3/common/extension"
    38  	"dubbo.apache.org/dubbo-go/v3/registry"
    39  	"dubbo.apache.org/dubbo-go/v3/remoting"
    40  	"dubbo.apache.org/dubbo-go/v3/remoting/zookeeper"
    41  	"dubbo.apache.org/dubbo-go/v3/remoting/zookeeper/curator_discovery"
    42  )
    43  
    44  const (
    45  	rootPath = "/services"
    46  )
    47  
    48  func init() {
    49  	extension.SetServiceDiscovery(constant.ZookeeperKey, newZookeeperServiceDiscovery)
    50  }
    51  
    52  type zookeeperServiceDiscovery struct {
    53  	client              *gxzookeeper.ZookeeperClient
    54  	csd                 *curator_discovery.ServiceDiscovery
    55  	url                 *common.URL
    56  	wg                  sync.WaitGroup
    57  	cltLock             sync.Mutex
    58  	listenLock          sync.Mutex
    59  	done                chan struct{}
    60  	rootPath            string
    61  	listenNames         []string
    62  	instanceListenerMap map[string]*gxset.HashSet
    63  }
    64  
    65  // newZookeeperServiceDiscovery the constructor of newZookeeperServiceDiscovery
    66  func newZookeeperServiceDiscovery(url *common.URL) (registry.ServiceDiscovery, error) {
    67  	group := url.GetParam(constant.RegistryGroupKey, rootPath)
    68  	if !strings.HasPrefix(group, constant.PathSeparator) {
    69  		group = constant.PathSeparator + group
    70  	}
    71  
    72  	zksd := &zookeeperServiceDiscovery{
    73  		url:                 url,
    74  		rootPath:            group,
    75  		instanceListenerMap: make(map[string]*gxset.HashSet),
    76  	}
    77  	if err := zookeeper.ValidateZookeeperClient(zksd, url.Location); err != nil {
    78  		return nil, err
    79  	}
    80  	zksd.WaitGroup().Add(1) // zk client start successful, then wg +1
    81  	go zookeeper.HandleClientRestart(zksd)
    82  	zksd.csd = curator_discovery.NewServiceDiscovery(zksd.client, group)
    83  	return zksd, nil
    84  }
    85  
    86  // nolint
    87  func (zksd *zookeeperServiceDiscovery) ZkClient() *gxzookeeper.ZookeeperClient {
    88  	return zksd.client
    89  }
    90  
    91  // nolint
    92  func (zksd *zookeeperServiceDiscovery) SetZkClient(client *gxzookeeper.ZookeeperClient) {
    93  	zksd.client = client
    94  }
    95  
    96  // nolint
    97  func (zksd *zookeeperServiceDiscovery) ZkClientLock() *sync.Mutex {
    98  	return &zksd.cltLock
    99  }
   100  
   101  // nolint
   102  func (zksd *zookeeperServiceDiscovery) WaitGroup() *sync.WaitGroup {
   103  	return &zksd.wg
   104  }
   105  
   106  // nolint
   107  func (zksd *zookeeperServiceDiscovery) Done() chan struct{} {
   108  	return zksd.done
   109  }
   110  
   111  // RestartCallBack when zookeeper connection reconnect this function will be invoked.
   112  // try to re-register service, and listen services
   113  func (zksd *zookeeperServiceDiscovery) RestartCallBack() bool {
   114  	zksd.csd.ReRegisterServices()
   115  	zksd.listenLock.Lock()
   116  	defer zksd.listenLock.Unlock()
   117  	for _, name := range zksd.listenNames {
   118  		zksd.csd.ListenServiceEvent(name, zksd)
   119  	}
   120  	return true
   121  }
   122  
   123  // nolint
   124  func (zksd *zookeeperServiceDiscovery) GetURL() *common.URL {
   125  	return zksd.url
   126  }
   127  
   128  // nolint
   129  func (zksd *zookeeperServiceDiscovery) String() string {
   130  	return fmt.Sprintf("zookeeper-service-discovery[%s]", zksd.url)
   131  }
   132  
   133  // Destroy will destroy the clinet.
   134  func (zksd *zookeeperServiceDiscovery) Destroy() error {
   135  	zksd.csd.Close()
   136  	return nil
   137  }
   138  
   139  // Register will register service in zookeeper, instance convert to curator's service instance
   140  // which define in curator-x-discovery.
   141  func (zksd *zookeeperServiceDiscovery) Register(instance registry.ServiceInstance) error {
   142  	cris := zksd.toCuratorInstance(instance)
   143  	return zksd.csd.RegisterService(cris)
   144  }
   145  
   146  // Update will update service in zookeeper, instance convert to curator's service instance
   147  // which define in curator-x-discovery, please refer to https://github.com/apache/curator.
   148  func (zksd *zookeeperServiceDiscovery) Update(instance registry.ServiceInstance) error {
   149  	cris := zksd.toCuratorInstance(instance)
   150  	return zksd.csd.UpdateService(cris)
   151  }
   152  
   153  // Unregister will unregister the instance in zookeeper
   154  func (zksd *zookeeperServiceDiscovery) Unregister(instance registry.ServiceInstance) error {
   155  	cris := zksd.toCuratorInstance(instance)
   156  	return zksd.csd.UnregisterService(cris)
   157  }
   158  
   159  // GetDefaultPageSize will return the constant registry.DefaultPageSize
   160  func (zksd *zookeeperServiceDiscovery) GetDefaultPageSize() int {
   161  	return registry.DefaultPageSize
   162  }
   163  
   164  // GetServices will return the all services in zookeeper
   165  func (zksd *zookeeperServiceDiscovery) GetServices() *gxset.HashSet {
   166  	services, err := zksd.csd.QueryForNames()
   167  	res := gxset.NewSet()
   168  	if err != nil {
   169  		logger.Errorf("[zkServiceDiscovery] Could not query the services: %v", err)
   170  		return res
   171  	}
   172  	for _, service := range services {
   173  		res.Add(service)
   174  	}
   175  	return res
   176  }
   177  
   178  // GetInstances will return the instances in a service
   179  func (zksd *zookeeperServiceDiscovery) GetInstances(serviceName string) []registry.ServiceInstance {
   180  	criss, err := zksd.csd.QueryForInstances(serviceName)
   181  	if err != nil {
   182  		logger.Errorf("[zkServiceDiscovery] Could not query the instances for service{%s}, error = err{%v} ",
   183  			serviceName, err)
   184  		return make([]registry.ServiceInstance, 0)
   185  	}
   186  	iss := make([]registry.ServiceInstance, 0, len(criss))
   187  	for _, cris := range criss {
   188  		iss = append(iss, toZookeeperInstance(cris))
   189  	}
   190  	return iss
   191  }
   192  
   193  // GetInstancesByPage will return the instances
   194  func (zksd *zookeeperServiceDiscovery) GetInstancesByPage(serviceName string, offset int, pageSize int) gxpage.Pager {
   195  	all := zksd.GetInstances(serviceName)
   196  	res := make([]interface{}, 0, pageSize)
   197  	// could not use res = all[a:b] here because the res should be []interface{}, not []ServiceInstance
   198  	for i := offset; i < len(all) && i < offset+pageSize; i++ {
   199  		res = append(res, all[i])
   200  	}
   201  	return gxpage.NewPage(offset, pageSize, res, len(all))
   202  }
   203  
   204  // GetHealthyInstancesByPage will return the instance
   205  // In zookeeper, all service instance's is healthy.
   206  // However, the healthy parameter in this method maybe false. So we can not use that API.
   207  // Thus, we must query all instances and then do filter
   208  func (zksd *zookeeperServiceDiscovery) GetHealthyInstancesByPage(serviceName string, offset int, pageSize int, healthy bool) gxpage.Pager {
   209  	all := zksd.GetInstances(serviceName)
   210  	res := make([]interface{}, 0, pageSize)
   211  	// could not use res = all[a:b] here because the res should be []interface{}, not []ServiceInstance
   212  	var (
   213  		i     = offset
   214  		count = 0
   215  	)
   216  	for i < len(all) && count < pageSize {
   217  		ins := all[i]
   218  		if ins.IsHealthy() == healthy {
   219  			res = append(res, all[i])
   220  			count++
   221  		}
   222  		i++
   223  	}
   224  	return gxpage.NewPage(offset, pageSize, res, len(all))
   225  }
   226  
   227  // GetRequestInstances will return the instances
   228  func (zksd *zookeeperServiceDiscovery) GetRequestInstances(serviceNames []string, offset int, requestedSize int) map[string]gxpage.Pager {
   229  	res := make(map[string]gxpage.Pager, len(serviceNames))
   230  	for _, name := range serviceNames {
   231  		res[name] = zksd.GetInstancesByPage(name, offset, requestedSize)
   232  	}
   233  	return res
   234  }
   235  
   236  // AddListener ListenServiceEvent will add a data listener in service
   237  func (zksd *zookeeperServiceDiscovery) AddListener(listener registry.ServiceInstancesChangedListener) error {
   238  	zksd.listenLock.Lock()
   239  	defer zksd.listenLock.Unlock()
   240  
   241  	for _, t := range listener.GetServiceNames().Values() {
   242  		serviceName, ok := t.(string)
   243  		if !ok {
   244  			logger.Errorf("service name error %s", t)
   245  			continue
   246  		}
   247  		zksd.listenNames = append(zksd.listenNames, serviceName)
   248  		listenerSet, found := zksd.instanceListenerMap[serviceName]
   249  		if !found {
   250  			listenerSet = gxset.NewSet(listener)
   251  			listenerSet.Add(listener)
   252  			zksd.instanceListenerMap[serviceName] = listenerSet
   253  		} else {
   254  			listenerSet.Add(listener)
   255  		}
   256  	}
   257  
   258  	for _, t := range listener.GetServiceNames().Values() {
   259  		serviceName, ok := t.(string)
   260  		if !ok {
   261  			logger.Errorf("service name error %s", t)
   262  			continue
   263  		}
   264  		zksd.csd.ListenServiceEvent(serviceName, zksd)
   265  	}
   266  	return nil
   267  }
   268  
   269  // DataChange implement DataListener's DataChange function
   270  // to resolve event to do DispatchEventByServiceName
   271  func (zksd *zookeeperServiceDiscovery) DataChange(eventType remoting.Event) bool {
   272  	path := strings.TrimPrefix(eventType.Path, zksd.rootPath)
   273  	path = strings.TrimPrefix(path, constant.PathSeparator)
   274  	// get service name in zk path
   275  	serviceName := strings.Split(path, constant.PathSeparator)[0]
   276  
   277  	var err error
   278  	instances := zksd.GetInstances(serviceName)
   279  	for _, lis := range zksd.instanceListenerMap[serviceName].Values() {
   280  		var instanceListener registry.ServiceInstancesChangedListener
   281  		instanceListener = lis.(registry.ServiceInstancesChangedListener)
   282  		err = instanceListener.OnEvent(registry.NewServiceInstancesChangedEvent(serviceName, instances))
   283  	}
   284  
   285  	if err != nil {
   286  		logger.Errorf("[zkServiceDiscovery] DispatchEventByServiceName{%s} error = err{%v}", serviceName, err)
   287  		return false
   288  	}
   289  	return true
   290  }
   291  
   292  // toCuratorInstance convert to curator's service instance
   293  func (zksd *zookeeperServiceDiscovery) toCuratorInstance(instance registry.ServiceInstance) *curator_discovery.ServiceInstance {
   294  	id := instance.GetHost() + ":" + strconv.Itoa(instance.GetPort())
   295  	pl := make(map[string]interface{}, 8)
   296  	pl["id"] = id
   297  	pl["name"] = instance.GetServiceName()
   298  	pl["metadata"] = instance.GetMetadata()
   299  	pl["@class"] = "org.apache.dubbo.registry.zookeeper.ZookeeperInstance"
   300  	cuis := &curator_discovery.ServiceInstance{
   301  		Name:                instance.GetServiceName(),
   302  		ID:                  id,
   303  		Address:             instance.GetHost(),
   304  		Port:                instance.GetPort(),
   305  		Payload:             pl,
   306  		RegistrationTimeUTC: 0,
   307  		Tag:                 instance.GetTag(),
   308  	}
   309  	return cuis
   310  }
   311  
   312  // toZookeeperInstance convert to registry's service instance
   313  func toZookeeperInstance(cris *curator_discovery.ServiceInstance) registry.ServiceInstance {
   314  	pl, ok := cris.Payload.(map[string]interface{})
   315  	if !ok {
   316  		logger.Errorf("[zkServiceDiscovery] toZookeeperInstance{%s} payload is not map[string]interface{}", cris.ID)
   317  		return nil
   318  	}
   319  	mdi, ok := pl["metadata"].(map[string]interface{})
   320  	if !ok {
   321  		logger.Errorf("[zkServiceDiscovery] toZookeeperInstance{%s} metadata is not map[string]interface{}", cris.ID)
   322  		return nil
   323  	}
   324  	md := make(map[string]string, len(mdi))
   325  	for k, v := range mdi {
   326  		md[k] = fmt.Sprint(v)
   327  	}
   328  	return &registry.DefaultServiceInstance{
   329  		ID:          cris.ID,
   330  		ServiceName: cris.Name,
   331  		Host:        cris.Address,
   332  		Port:        cris.Port,
   333  		Enable:      true,
   334  		Healthy:     true,
   335  		Metadata:    md,
   336  		Tag:         cris.Tag,
   337  	}
   338  }