dubbo.apache.org/dubbo-go/v3@v3.1.1/registry/nacos/registry.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 nacos
    19  
    20  import (
    21  	"bytes"
    22  	"fmt"
    23  	"strconv"
    24  	"strings"
    25  	"time"
    26  )
    27  
    28  import (
    29  	nacosClient "github.com/dubbogo/gost/database/kv/nacos"
    30  	"github.com/dubbogo/gost/log/logger"
    31  
    32  	"github.com/nacos-group/nacos-sdk-go/v2/vo"
    33  
    34  	perrors "github.com/pkg/errors"
    35  )
    36  
    37  import (
    38  	"dubbo.apache.org/dubbo-go/v3/common"
    39  	"dubbo.apache.org/dubbo-go/v3/common/constant"
    40  	"dubbo.apache.org/dubbo-go/v3/common/extension"
    41  	"dubbo.apache.org/dubbo-go/v3/metrics"
    42  	metricsRegistry "dubbo.apache.org/dubbo-go/v3/metrics/registry"
    43  	"dubbo.apache.org/dubbo-go/v3/registry"
    44  	"dubbo.apache.org/dubbo-go/v3/remoting"
    45  	"dubbo.apache.org/dubbo-go/v3/remoting/nacos"
    46  )
    47  
    48  const (
    49  	RegistryConnDelay = 3
    50  )
    51  
    52  func init() {
    53  	extension.SetRegistry(constant.NacosKey, newNacosRegistry)
    54  }
    55  
    56  type nacosRegistry struct {
    57  	*common.URL
    58  	namingClient *nacosClient.NacosNamingClient
    59  	registryUrls []*common.URL
    60  }
    61  
    62  func getCategory(url *common.URL) string {
    63  	role, _ := strconv.Atoi(url.GetParam(constant.RegistryRoleKey, strconv.Itoa(constant.NacosDefaultRoleType)))
    64  	category := common.DubboNodes[role]
    65  	return category
    66  }
    67  
    68  func getServiceName(url *common.URL) string {
    69  	var buffer bytes.Buffer
    70  
    71  	buffer.Write([]byte(getCategory(url)))
    72  	appendParam(&buffer, url, constant.InterfaceKey)
    73  	appendParam(&buffer, url, constant.VersionKey)
    74  	appendParam(&buffer, url, constant.GroupKey)
    75  	return buffer.String()
    76  }
    77  
    78  func appendParam(target *bytes.Buffer, url *common.URL, key string) {
    79  	value := url.GetParam(key, "")
    80  	target.Write([]byte(constant.NacosServiceNameSeparator))
    81  	if strings.TrimSpace(value) != "" {
    82  		target.Write([]byte(value))
    83  	}
    84  }
    85  
    86  func createRegisterParam(url *common.URL, serviceName string, groupName string) vo.RegisterInstanceParam {
    87  	category := getCategory(url)
    88  	params := make(map[string]string)
    89  
    90  	url.RangeParams(func(key, value string) bool {
    91  		params[key] = value
    92  		return true
    93  	})
    94  
    95  	params[constant.NacosCategoryKey] = category
    96  	params[constant.NacosProtocolKey] = url.Protocol
    97  	params[constant.NacosPathKey] = url.Path
    98  	params[constant.MethodsKey] = strings.Join(url.Methods, ",")
    99  	common.HandleRegisterIPAndPort(url)
   100  	port, _ := strconv.Atoi(url.Port)
   101  	instance := vo.RegisterInstanceParam{
   102  		Ip:          url.Ip,
   103  		Port:        uint64(port),
   104  		Metadata:    params,
   105  		Weight:      1,
   106  		Enable:      true,
   107  		Healthy:     true,
   108  		Ephemeral:   true,
   109  		ServiceName: serviceName,
   110  		GroupName:   groupName,
   111  	}
   112  	return instance
   113  }
   114  
   115  // Register will register the service @url to its nacos registry center.
   116  func (nr *nacosRegistry) Register(url *common.URL) error {
   117  	start := time.Now()
   118  	serviceName := getServiceName(url)
   119  	groupName := nr.URL.GetParam(constant.NacosGroupKey, defaultGroup)
   120  	param := createRegisterParam(url, serviceName, groupName)
   121  	logger.Infof("[Nacos Registry] Registry instance with param = %+v", param)
   122  	isRegistry, err := nr.namingClient.Client().RegisterInstance(param)
   123  	metrics.Publish(metricsRegistry.NewRegisterEvent(err == nil && isRegistry, start))
   124  	if err != nil {
   125  		return err
   126  	}
   127  	if !isRegistry {
   128  		return perrors.New("registry [" + serviceName + "] to  nacos failed")
   129  	}
   130  	nr.registryUrls = append(nr.registryUrls, url)
   131  	return nil
   132  }
   133  
   134  func createDeregisterParam(url *common.URL, serviceName string, groupName string) vo.DeregisterInstanceParam {
   135  	common.HandleRegisterIPAndPort(url)
   136  	port, _ := strconv.Atoi(url.Port)
   137  	return vo.DeregisterInstanceParam{
   138  		Ip:          url.Ip,
   139  		Port:        uint64(port),
   140  		ServiceName: serviceName,
   141  		GroupName:   groupName,
   142  		Ephemeral:   true,
   143  	}
   144  }
   145  
   146  // UnRegister returns nil if unregister successfully. If not, returns an error.
   147  func (nr *nacosRegistry) UnRegister(url *common.URL) error {
   148  	serviceName := getServiceName(url)
   149  	groupName := nr.URL.GetParam(constant.NacosGroupKey, defaultGroup)
   150  	param := createDeregisterParam(url, serviceName, groupName)
   151  	isDeRegistry, err := nr.namingClient.Client().DeregisterInstance(param)
   152  	if err != nil {
   153  		return err
   154  	}
   155  	if !isDeRegistry {
   156  		return perrors.New("DeRegistry [" + serviceName + "] to nacos failed")
   157  	}
   158  	return nil
   159  }
   160  
   161  func (nr *nacosRegistry) subscribe(conf *common.URL) (registry.Listener, error) {
   162  	return NewNacosListener(conf, nr.URL, nr.namingClient)
   163  }
   164  
   165  // Subscribe returns nil if subscribing registry successfully. If not returns an error.
   166  func (nr *nacosRegistry) Subscribe(url *common.URL, notifyListener registry.NotifyListener) error {
   167  	// TODO
   168  	role, _ := strconv.Atoi(url.GetParam(constant.RegistryRoleKey, ""))
   169  	if role != common.CONSUMER {
   170  		return nil
   171  	}
   172  	serviceName := getServiceName(url)
   173  	if serviceName == "*" {
   174  		// Subscribe to all services
   175  		for {
   176  			if !nr.IsAvailable() {
   177  				logger.Warnf("event listener game over.")
   178  				return perrors.New("nacosRegistry is not available.")
   179  			}
   180  
   181  			services, err := nr.getAllSubscribeServiceNames()
   182  			if err != nil {
   183  				if !nr.IsAvailable() {
   184  					logger.Warnf("event listener game over.")
   185  					return err
   186  				}
   187  				logger.Warnf("getAllServices() = err:%v", perrors.WithStack(err))
   188  				time.Sleep(time.Duration(RegistryConnDelay) * time.Second)
   189  				continue
   190  			}
   191  
   192  			for _, service := range services {
   193  				listener, err := nr.subscribeToService(url, service)
   194  				metrics.Publish(metricsRegistry.NewSubscribeEvent(err == nil))
   195  				if err != nil {
   196  					logger.Warnf("Failed to subscribe to service '%s': %v", service, err)
   197  					continue
   198  				}
   199  
   200  				nr.handleServiceEvents(listener, notifyListener)
   201  			}
   202  		}
   203  	} else {
   204  		// Subscribe to a specific service
   205  		for {
   206  			if !nr.IsAvailable() {
   207  				logger.Warnf("event listener game over.")
   208  				return perrors.New("nacosRegistry is not available.")
   209  			}
   210  
   211  			listener, err := nr.subscribe(url)
   212  			metrics.Publish(metricsRegistry.NewSubscribeEvent(err == nil))
   213  			if err != nil {
   214  				if !nr.IsAvailable() {
   215  					logger.Warnf("event listener game over.")
   216  					return err
   217  				}
   218  				logger.Warnf("getListener() = err:%v", perrors.WithStack(err))
   219  				time.Sleep(time.Duration(RegistryConnDelay) * time.Second)
   220  				continue
   221  			}
   222  			nr.handleServiceEvents(listener, notifyListener)
   223  		}
   224  	}
   225  }
   226  
   227  // getAllServices retrieves the list of all services from the registry
   228  func (nr *nacosRegistry) getAllSubscribeServiceNames() ([]string, error) {
   229  	services, err := nr.namingClient.Client().GetAllServicesInfo(vo.GetAllServiceInfoParam{
   230  		GroupName: nr.GetParam(constant.RegistryGroupKey, defaultGroup),
   231  		PageNo:    1,
   232  		PageSize:  10,
   233  	})
   234  	subScribeServiceNames := []string{}
   235  	for _, dom := range services.Doms {
   236  		if strings.HasPrefix(dom, "providers:") {
   237  			subScribeServiceNames = append(subScribeServiceNames, dom)
   238  		}
   239  	}
   240  
   241  	return subScribeServiceNames, err
   242  }
   243  
   244  // subscribeToService subscribes to a specific service in the registry
   245  func (nr *nacosRegistry) subscribeToService(url *common.URL, service string) (listener registry.Listener, err error) {
   246  	return NewNacosListenerWithServiceName(service, url, nr.URL, nr.namingClient)
   247  }
   248  
   249  // handleServiceEvents receives service events from the listener and notifies the notifyListener
   250  func (nr *nacosRegistry) handleServiceEvents(listener registry.Listener, notifyListener registry.NotifyListener) {
   251  	for {
   252  		serviceEvent, err := listener.Next()
   253  		if err != nil {
   254  			logger.Warnf("Selector.watch() = error{%v}", perrors.WithStack(err))
   255  			listener.Close()
   256  			return
   257  		}
   258  		logger.Infof("[Nacos Registry] Update begin, service event: %v", serviceEvent.String())
   259  		notifyListener.Notify(serviceEvent)
   260  	}
   261  }
   262  
   263  // UnSubscribe :
   264  func (nr *nacosRegistry) UnSubscribe(url *common.URL, _ registry.NotifyListener) error {
   265  	param := createSubscribeParam(url, nr.URL, nil)
   266  	if param == nil {
   267  		return nil
   268  	}
   269  	err := nr.namingClient.Client().Unsubscribe(param)
   270  	if err != nil {
   271  		return perrors.New("UnSubscribe [" + param.ServiceName + "] to nacos failed")
   272  	}
   273  	return nil
   274  }
   275  
   276  // LoadSubscribeInstances load subscribe instance
   277  func (nr *nacosRegistry) LoadSubscribeInstances(url *common.URL, notify registry.NotifyListener) error {
   278  	serviceName := getSubscribeName(url)
   279  	groupName := nr.GetURL().GetParam(constant.RegistryGroupKey, defaultGroup)
   280  	instances, err := nr.namingClient.Client().SelectAllInstances(vo.SelectAllInstancesParam{
   281  		ServiceName: serviceName,
   282  		GroupName:   groupName,
   283  	})
   284  	if err != nil {
   285  		return perrors.New(fmt.Sprintf("could not query the instances for serviceName=%s,groupName=%s,error=%v",
   286  			serviceName, groupName, err))
   287  	}
   288  
   289  	for i := range instances {
   290  		if newUrl := generateUrl(instances[i]); newUrl != nil {
   291  			notify.Notify(&registry.ServiceEvent{Action: remoting.EventTypeAdd, Service: newUrl})
   292  		}
   293  	}
   294  	return nil
   295  }
   296  
   297  func createSubscribeParam(url, regUrl *common.URL, cb callback) *vo.SubscribeParam {
   298  	serviceName := getSubscribeName(url)
   299  	groupName := regUrl.GetParam(constant.RegistryGroupKey, defaultGroup)
   300  	if cb == nil {
   301  		v, ok := listenerCache.Load(serviceName + groupName)
   302  		if !ok {
   303  			return nil
   304  		}
   305  		listener, ok := v.(*nacosListener)
   306  		if !ok {
   307  			return nil
   308  		}
   309  		cb = listener.Callback
   310  	}
   311  	return &vo.SubscribeParam{
   312  		ServiceName:       serviceName,
   313  		SubscribeCallback: cb,
   314  		GroupName:         groupName,
   315  	}
   316  }
   317  
   318  func createSubscribeParamWithServiceName(serviceName string, regUrl *common.URL, cb callback) *vo.SubscribeParam {
   319  	groupName := regUrl.GetParam(constant.RegistryGroupKey, defaultGroup)
   320  	if cb == nil {
   321  		v, ok := listenerCache.Load(serviceName + groupName)
   322  		if !ok {
   323  			return nil
   324  		}
   325  		listener, ok := v.(*nacosListener)
   326  		if !ok {
   327  			return nil
   328  		}
   329  		cb = listener.Callback
   330  	}
   331  	return &vo.SubscribeParam{
   332  		ServiceName:       serviceName,
   333  		SubscribeCallback: cb,
   334  		GroupName:         groupName,
   335  	}
   336  }
   337  
   338  // GetURL gets its registration URL
   339  func (nr *nacosRegistry) GetURL() *common.URL {
   340  	return nr.URL
   341  }
   342  
   343  // IsAvailable determines nacos registry center whether it is available
   344  func (nr *nacosRegistry) IsAvailable() bool {
   345  	// TODO
   346  	return true
   347  }
   348  
   349  // nolint
   350  func (nr *nacosRegistry) Destroy() {
   351  	for _, url := range nr.registryUrls {
   352  		err := nr.UnRegister(url)
   353  		logger.Infof("DeRegister Nacos URL:%+v", url)
   354  		if err != nil {
   355  			logger.Errorf("Deregister URL:%+v err:%v", url, err.Error())
   356  		}
   357  	}
   358  	return
   359  }
   360  
   361  // newNacosRegistry will create new instance
   362  func newNacosRegistry(url *common.URL) (registry.Registry, error) {
   363  	logger.Infof("[Nacos Registry] New nacos registry with url = %+v", url.ToMap())
   364  	// key transfer: registry -> nacos
   365  	url.SetParam(constant.NacosNamespaceID, url.GetParam(constant.RegistryNamespaceKey, ""))
   366  	url.SetParam(constant.NacosUsername, url.Username)
   367  	url.SetParam(constant.NacosPassword, url.Password)
   368  	url.SetParam(constant.NacosAccessKey, url.GetParam(constant.RegistryAccessKey, ""))
   369  	url.SetParam(constant.NacosSecretKey, url.GetParam(constant.RegistrySecretKey, ""))
   370  	url.SetParam(constant.NacosTimeout, url.GetParam(constant.RegistryTimeoutKey, constant.DefaultRegTimeout))
   371  	url.SetParam(constant.NacosGroupKey, url.GetParam(constant.RegistryGroupKey, defaultGroup))
   372  	namingClient, err := nacos.NewNacosClientByURL(url)
   373  	if err != nil {
   374  		return &nacosRegistry{}, err
   375  	}
   376  	tmpRegistry := &nacosRegistry{
   377  		URL:          url, // registry.group is recorded at this url
   378  		namingClient: namingClient,
   379  		registryUrls: []*common.URL{},
   380  	}
   381  	return tmpRegistry, nil
   382  }