dubbo.apache.org/dubbo-go/v3@v3.1.1/registry/base_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 registry
    19  
    20  import (
    21  	"fmt"
    22  	"net/url"
    23  	"os"
    24  	"strconv"
    25  	"strings"
    26  	"sync"
    27  	"time"
    28  )
    29  
    30  import (
    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"
    38  	"dubbo.apache.org/dubbo-go/v3/common/constant"
    39  	"dubbo.apache.org/dubbo-go/v3/metrics"
    40  	metricsRegistry "dubbo.apache.org/dubbo-go/v3/metrics/registry"
    41  )
    42  
    43  const (
    44  	RegistryConnDelay = 3               // connection delay
    45  	MaxWaitInterval   = 3 * time.Second // max wait interval
    46  )
    47  
    48  var (
    49  	processID = ""
    50  	localIP   = ""
    51  )
    52  
    53  func init() {
    54  	processID = fmt.Sprintf("%d", os.Getpid())
    55  	localIP = common.GetLocalIp()
    56  }
    57  
    58  type createPathFunc func(dubboPath string) error
    59  
    60  // FacadeBasedRegistry is the interface of Registry, and it is designed for registry who
    61  // want to inherit BaseRegistry. If there is no special case, you'd better inherit BaseRegistry
    62  // and implement the FacadeBasedRegistry interface instead of directly implementing the
    63  // Registry interface.
    64  //
    65  // CreatePath method create the path in the registry.
    66  //
    67  // DoRegister method actually does the register job.
    68  //
    69  // DoUnregister method does the unregister job.
    70  //
    71  // DoSubscribe method actually subscribes the URL.
    72  //
    73  // DoUnsubscribe method does unsubscribe the URL.
    74  //
    75  // CloseAndNilClient method closes the client and then reset the client in registry to nil
    76  // you should notice that this method will be invoked inside a lock.
    77  // So you should implement this method as light weighted as you can.
    78  //
    79  // CloseListener method closes listeners.
    80  //
    81  // InitListeners method init listeners
    82  type FacadeBasedRegistry interface {
    83  	Registry
    84  
    85  	CreatePath(string) error
    86  	DoRegister(string, string) error
    87  	DoUnregister(string, string) error
    88  	DoSubscribe(conf *common.URL) (Listener, error)
    89  	DoUnsubscribe(conf *common.URL) (Listener, error)
    90  	CloseAndNilClient()
    91  	CloseListener()
    92  	InitListeners()
    93  }
    94  
    95  // BaseRegistry is a common logic abstract for registry. It implement Registry interface.
    96  type BaseRegistry struct {
    97  	facadeBasedRegistry FacadeBasedRegistry
    98  	*common.URL
    99  	birth      int64          // time of file birth, seconds since Epoch; 0 if unknown
   100  	wg         sync.WaitGroup // wg+done for zk restart
   101  	done       chan struct{}
   102  	registered *sync.Map
   103  	cltLock    sync.RWMutex
   104  }
   105  
   106  // InitBaseRegistry for init some local variables and set BaseRegistry's subclass to it
   107  func (r *BaseRegistry) InitBaseRegistry(url *common.URL, facadeRegistry FacadeBasedRegistry) Registry {
   108  	r.URL = url
   109  	r.birth = time.Now().UnixNano()
   110  	r.done = make(chan struct{})
   111  	r.registered = &sync.Map{}
   112  	r.facadeBasedRegistry = facadeRegistry
   113  	return r
   114  }
   115  
   116  // GetURL for get registry's url
   117  func (r *BaseRegistry) GetURL() *common.URL {
   118  	return r.URL
   119  }
   120  
   121  // Destroy for graceful down
   122  func (r *BaseRegistry) Destroy() {
   123  	// first step close registry's all listeners
   124  	r.facadeBasedRegistry.CloseListener()
   125  	// then close r.done to notify other program who listen to it
   126  	close(r.Done())
   127  	// wait waitgroup done (wait listeners outside close over)
   128  	r.WaitGroup().Wait()
   129  
   130  	// close registry client
   131  	r.closeRegisters()
   132  }
   133  
   134  // Register implement interface registry to register
   135  func (r *BaseRegistry) Register(url *common.URL) error {
   136  	start := time.Now()
   137  	// todo bug when provider、consumer simultaneous initialization
   138  	if _, ok := r.registered.Load(url.Key()); ok {
   139  		return perrors.Errorf("Service {%s} has been registered", url.Key())
   140  	}
   141  
   142  	err := r.register(url)
   143  	defer metrics.Publish(metricsRegistry.NewRegisterEvent(err == nil, start))
   144  	if err == nil {
   145  		r.registered.Store(url.Key(), url)
   146  
   147  	} else {
   148  		err = perrors.WithMessagef(err, "register(url:%+v)", url)
   149  	}
   150  
   151  	return err
   152  }
   153  
   154  // UnRegister implement interface registry to unregister
   155  func (r *BaseRegistry) UnRegister(url *common.URL) error {
   156  	if _, ok := r.registered.Load(url.Key()); !ok {
   157  		return perrors.Errorf("Service {%s} has not registered", url.Key())
   158  	}
   159  	err := r.unregister(url)
   160  	metrics.Publish(metricsRegistry.NewSubscribeEvent(err == nil))
   161  	if err == nil {
   162  		r.registered.Delete(url.Key())
   163  	} else {
   164  		err = perrors.WithMessagef(err, "unregister(url:%+v)", url)
   165  	}
   166  
   167  	return err
   168  }
   169  
   170  // service is for getting service path stored in url
   171  func (r *BaseRegistry) service(c *common.URL) string {
   172  	return url.QueryEscape(c.Service())
   173  }
   174  
   175  // RestartCallBack for reregister when reconnect
   176  func (r *BaseRegistry) RestartCallBack() bool {
   177  	flag := true
   178  	r.registered.Range(func(key, value interface{}) bool {
   179  		registeredUrl := value.(*common.URL)
   180  		err := r.register(registeredUrl)
   181  		if err != nil {
   182  			flag = false
   183  			logger.Errorf("failed to re-register service :%v, error{%#v}",
   184  				registeredUrl, perrors.WithStack(err))
   185  			return flag
   186  		}
   187  
   188  		logger.Infof("success to re-register service :%v", registeredUrl.Key())
   189  		return flag
   190  	})
   191  
   192  	if flag {
   193  		r.facadeBasedRegistry.InitListeners()
   194  	}
   195  
   196  	return flag
   197  }
   198  
   199  // register for register url to registry, include init params
   200  func (r *BaseRegistry) register(c *common.URL) error {
   201  	return r.processURL(c, r.facadeBasedRegistry.DoRegister, r.createPath)
   202  }
   203  
   204  // unregister for unregister url to registry, include init params
   205  func (r *BaseRegistry) unregister(c *common.URL) error {
   206  	return r.processURL(c, r.facadeBasedRegistry.DoUnregister, nil)
   207  }
   208  
   209  func (r *BaseRegistry) processURL(c *common.URL, f func(string, string) error, cpf createPathFunc) error {
   210  	if f == nil {
   211  		panic(" Must provide a `function(string, string) error` to process URL. ")
   212  	}
   213  	var (
   214  		err        error
   215  		params     url.Values
   216  		rawURL     string
   217  		encodedURL string
   218  		dubboPath  string
   219  	)
   220  	params = url.Values{}
   221  
   222  	c.RangeParams(func(key, value string) bool {
   223  		params.Add(key, value)
   224  		return true
   225  	})
   226  
   227  	role, _ := strconv.Atoi(c.GetParam(constant.RegistryRoleKey, ""))
   228  	switch role {
   229  	case common.PROVIDER:
   230  		dubboPath, rawURL, err = r.providerRegistry(c, params, cpf)
   231  	case common.CONSUMER:
   232  		dubboPath, rawURL, err = r.consumerRegistry(c, params, cpf)
   233  	default:
   234  		return perrors.Errorf("@c{%v} type is not referencer or provider", c)
   235  	}
   236  	if err != nil {
   237  		return perrors.WithMessagef(err, "@c{%v} registry fail", c)
   238  	}
   239  
   240  	encodedURL = url.QueryEscape(rawURL)
   241  	dubboPath = strings.ReplaceAll(dubboPath, "$", "%24")
   242  	err = f(dubboPath, encodedURL)
   243  
   244  	if err != nil {
   245  		return perrors.WithMessagef(err, "register Node(path:%s, url:%s)", dubboPath, rawURL)
   246  	}
   247  	return nil
   248  }
   249  
   250  // createPath will create dubbo path in register
   251  func (r *BaseRegistry) createPath(dubboPath string) error {
   252  	r.cltLock.Lock()
   253  	defer r.cltLock.Unlock()
   254  	return r.facadeBasedRegistry.CreatePath(dubboPath)
   255  }
   256  
   257  // providerRegistry for provider role do
   258  func (r *BaseRegistry) providerRegistry(c *common.URL, params url.Values, f createPathFunc) (string, string, error) {
   259  	var (
   260  		dubboPath string
   261  		rawURL    string
   262  		err       error
   263  	)
   264  	if c.Path == "" || len(c.Methods) == 0 {
   265  		return "", "", perrors.Errorf("conf{Path:%s, Methods:%s}", c.Path, c.Methods)
   266  	}
   267  	dubboPath = fmt.Sprintf("/%s/%s/%s", r.URL.GetParam(constant.RegistryGroupKey, "dubbo"), r.service(c), common.DubboNodes[common.PROVIDER])
   268  	if f != nil {
   269  		err = f(dubboPath)
   270  	}
   271  	if err != nil {
   272  		logger.Errorf("facadeBasedRegistry.CreatePath(path{%s}) = error{%#v}", dubboPath, perrors.WithStack(err))
   273  		return "", "", perrors.WithMessagef(err, "facadeBasedRegistry.CreatePath(path:%s)", dubboPath)
   274  	}
   275  	params.Add(constant.AnyhostKey, "true")
   276  
   277  	// Dubbo java consumer to start looking for the provider url,because the category does not match,
   278  	// the provider will not find, causing the consumer can not start, so we use consumers.
   279  
   280  	if len(c.Methods) != 0 {
   281  		params.Add(constant.MethodsKey, strings.Join(c.Methods, ","))
   282  	}
   283  	logger.Debugf("provider url params:%#v", params)
   284  	var host string
   285  	if len(c.Ip) == 0 {
   286  		host = localIP
   287  	} else {
   288  		host = c.Ip
   289  	}
   290  	host += ":" + c.Port
   291  
   292  	// delete empty param key
   293  	for key, val := range params {
   294  		if len(val) > 0 && val[0] == "" {
   295  			params.Del(key)
   296  		}
   297  	}
   298  
   299  	s, _ := url.QueryUnescape(params.Encode())
   300  	rawURL = fmt.Sprintf("%s://%s%s?%s", c.Protocol, host, c.Path, s)
   301  	// Print your own registration service providers.
   302  	logger.Debugf("provider path:%s, url:%s", dubboPath, rawURL)
   303  	return dubboPath, rawURL, nil
   304  }
   305  
   306  // consumerRegistry for consumer role do
   307  func (r *BaseRegistry) consumerRegistry(c *common.URL, params url.Values, f createPathFunc) (string, string, error) {
   308  	var (
   309  		dubboPath string
   310  		rawURL    string
   311  		err       error
   312  	)
   313  	dubboPath = fmt.Sprintf("/%s/%s/%s", r.URL.GetParam(constant.RegistryGroupKey, "dubbo"), r.service(c), common.DubboNodes[common.CONSUMER])
   314  
   315  	if f != nil {
   316  		err = f(dubboPath)
   317  	}
   318  
   319  	if err != nil {
   320  		logger.Errorf("facadeBasedRegistry.CreatePath(path{%s}) = error{%v}", dubboPath, perrors.WithStack(err))
   321  		return "", "", perrors.WithStack(err)
   322  	}
   323  
   324  	params.Add("protocol", c.Protocol)
   325  	s, _ := url.QueryUnescape(params.Encode())
   326  	rawURL = fmt.Sprintf("consumer://%s%s?%s", localIP, c.Path, s)
   327  	logger.Debugf("consumer path:%s, url:%s", dubboPath, rawURL)
   328  	return dubboPath, rawURL, nil
   329  }
   330  
   331  // sleepWait...
   332  func sleepWait(n int) {
   333  	wait := time.Duration((n + 1) * 2e8)
   334  	if wait > MaxWaitInterval {
   335  		wait = MaxWaitInterval
   336  	}
   337  	time.Sleep(wait)
   338  }
   339  
   340  // Subscribe :subscribe from registry, event will notify by notifyListener
   341  func (r *BaseRegistry) Subscribe(url *common.URL, notifyListener NotifyListener) error {
   342  	for {
   343  		if !r.IsAvailable() {
   344  			logger.Warnf("event listener game over.")
   345  			return perrors.New("BaseRegistry is not available.")
   346  		}
   347  
   348  		listener, err := r.facadeBasedRegistry.DoSubscribe(url)
   349  		if err != nil {
   350  			if !r.IsAvailable() {
   351  				logger.Warnf("event listener game over.")
   352  				return err
   353  			}
   354  			logger.Warnf("getListener() = err:%v", perrors.WithStack(err))
   355  			time.Sleep(time.Duration(RegistryConnDelay) * time.Second)
   356  			continue
   357  		}
   358  
   359  		for {
   360  			if serviceEvent, err := listener.Next(); err != nil {
   361  				logger.Warnf("Selector.watch() = error{%v}", perrors.WithStack(err))
   362  				listener.Close()
   363  				return nil
   364  			} else {
   365  				logger.Debugf("[Registry] update begin, service event: %v", serviceEvent.String())
   366  				notifyListener.Notify(serviceEvent)
   367  			}
   368  		}
   369  	}
   370  }
   371  
   372  // UnSubscribe URL
   373  func (r *BaseRegistry) UnSubscribe(url *common.URL, notifyListener NotifyListener) error {
   374  	if !r.IsAvailable() {
   375  		logger.Warnf("event listener game over.")
   376  		return perrors.New("BaseRegistry is not available.")
   377  	}
   378  
   379  	listener, err := r.facadeBasedRegistry.DoUnsubscribe(url)
   380  	if err != nil {
   381  		if !r.IsAvailable() {
   382  			logger.Warnf("event listener game over.")
   383  			return perrors.New("BaseRegistry is not available.")
   384  		}
   385  		logger.Warnf("getListener() = err:%v", perrors.WithStack(err))
   386  		return perrors.WithStack(err)
   387  	}
   388  
   389  	if listener != nil {
   390  		listener.Close()
   391  	}
   392  
   393  	return nil
   394  }
   395  
   396  // LoadSubscribeInstances load subscribe instance
   397  func (r *BaseRegistry) LoadSubscribeInstances(url *common.URL, notify NotifyListener) error {
   398  	return r.facadeBasedRegistry.LoadSubscribeInstances(url, notify)
   399  }
   400  
   401  // closeRegisters close and remove registry client and reset services map
   402  func (r *BaseRegistry) closeRegisters() {
   403  	logger.Infof("begin to close provider client")
   404  	r.cltLock.Lock()
   405  	defer r.cltLock.Unlock()
   406  	// Close and remove(set to nil) the registry client
   407  	r.facadeBasedRegistry.CloseAndNilClient()
   408  	// reset the services map
   409  	r.registered = nil
   410  }
   411  
   412  // IsAvailable judge to is registry not closed by chan r.done
   413  func (r *BaseRegistry) IsAvailable() bool {
   414  	select {
   415  	case <-r.Done():
   416  		return false
   417  	default:
   418  		return true
   419  	}
   420  }
   421  
   422  // WaitGroup open for outside add the waitgroup to add some logic before registry destroyed over(graceful down)
   423  func (r *BaseRegistry) WaitGroup() *sync.WaitGroup {
   424  	return &r.wg
   425  }
   426  
   427  // Done open for outside to listen the event of registry Destroy() called.
   428  func (r *BaseRegistry) Done() chan struct{} {
   429  	return r.done
   430  }