dubbo.apache.org/dubbo-go/v3@v3.1.1/registry/polaris/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 polaris
    19  
    20  import (
    21  	"fmt"
    22  	"strconv"
    23  	"sync"
    24  	"time"
    25  )
    26  
    27  import (
    28  	"github.com/dubbogo/gost/log/logger"
    29  
    30  	perrors "github.com/pkg/errors"
    31  
    32  	api "github.com/polarismesh/polaris-go"
    33  	"github.com/polarismesh/polaris-go/pkg/model"
    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/common/extension"
    40  	"dubbo.apache.org/dubbo-go/v3/registry"
    41  	"dubbo.apache.org/dubbo-go/v3/remoting"
    42  	"dubbo.apache.org/dubbo-go/v3/remoting/polaris"
    43  )
    44  
    45  const (
    46  	RegistryConnDelay           = 3
    47  	defaultHeartbeatIntervalSec = 5
    48  )
    49  
    50  func init() {
    51  	extension.SetRegistry(constant.PolarisKey, newPolarisRegistry)
    52  }
    53  
    54  // newPolarisRegistry will create new instance
    55  func newPolarisRegistry(url *common.URL) (registry.Registry, error) {
    56  	if err := polaris.InitSDKContext(url); err != nil {
    57  		return &polarisRegistry{}, err
    58  	}
    59  
    60  	providerApi, err := polaris.GetProviderAPI()
    61  	if err != nil {
    62  		return nil, err
    63  	}
    64  
    65  	consumerApi, err := polaris.GetConsumerAPI()
    66  	if err != nil {
    67  		return nil, err
    68  	}
    69  
    70  	pRegistry := &polarisRegistry{
    71  		url:          url,
    72  		namespace:    url.GetParam(constant.RegistryNamespaceKey, constant.PolarisDefaultNamespace),
    73  		provider:     providerApi,
    74  		consumer:     consumerApi,
    75  		registryUrls: make([]*common.URL, 0, 4),
    76  		watchers:     map[string]*PolarisServiceWatcher{},
    77  	}
    78  
    79  	return pRegistry, nil
    80  }
    81  
    82  type polarisRegistry struct {
    83  	namespace    string
    84  	url          *common.URL
    85  	consumer     api.ConsumerAPI
    86  	provider     api.ProviderAPI
    87  	lock         sync.RWMutex
    88  	registryUrls []*common.URL
    89  	listenerLock sync.RWMutex
    90  	watchers     map[string]*PolarisServiceWatcher
    91  }
    92  
    93  // Register will register the service @url to its polaris registry center.
    94  func (pr *polarisRegistry) Register(url *common.URL) error {
    95  	if getCategory(url) != "providers" {
    96  		return nil
    97  	}
    98  
    99  	serviceName := url.Interface()
   100  	request := createRegisterParam(url, serviceName)
   101  	request.Namespace = pr.namespace
   102  	resp, err := pr.provider.RegisterInstance(request)
   103  	if err != nil {
   104  		return err
   105  	}
   106  
   107  	if resp.Existed {
   108  		logger.Warnf("instance already regist, namespace:%+v, service:%+v, host:%+v, port:%+v",
   109  			request.Namespace, request.Service, request.Host, request.Port)
   110  	}
   111  	url.SetParam(constant.PolarisInstanceID, resp.InstanceID)
   112  
   113  	pr.lock.Lock()
   114  	pr.registryUrls = append(pr.registryUrls, url)
   115  	pr.lock.Unlock()
   116  
   117  	return nil
   118  }
   119  
   120  // UnRegister returns nil if unregister successfully. If not, returns an error.
   121  func (pr *polarisRegistry) UnRegister(url *common.URL) error {
   122  	request := createDeregisterParam(url, url.Interface())
   123  	request.Namespace = pr.namespace
   124  	if err := pr.provider.Deregister(request); err != nil {
   125  		return perrors.WithMessagef(err, "fail to deregister(conf:%+v)", url)
   126  	}
   127  	return nil
   128  }
   129  
   130  // Subscribe returns nil if subscribing registry successfully. If not returns an error.
   131  func (pr *polarisRegistry) Subscribe(url *common.URL, notifyListener registry.NotifyListener) error {
   132  
   133  	role, _ := strconv.Atoi(url.GetParam(constant.RegistryRoleKey, ""))
   134  	if role != common.CONSUMER {
   135  		return nil
   136  	}
   137  	timer := time.NewTimer(time.Duration(RegistryConnDelay) * time.Second)
   138  	defer timer.Stop()
   139  
   140  	for {
   141  		serviceName := url.Interface()
   142  		watcher, err := pr.createPolarisWatcher(serviceName)
   143  		if err != nil {
   144  			logger.Warnf("getwatcher() = err:%v", perrors.WithStack(err))
   145  			<-timer.C
   146  			timer.Reset(time.Duration(RegistryConnDelay) * time.Second)
   147  			continue
   148  		}
   149  
   150  		listener, err := NewPolarisListener(watcher)
   151  		if err != nil {
   152  			logger.Warnf("getListener() = err:%v", perrors.WithStack(err))
   153  			<-timer.C
   154  			timer.Reset(time.Duration(RegistryConnDelay) * time.Second)
   155  			continue
   156  		}
   157  
   158  		for {
   159  			serviceEvent, err := listener.Next()
   160  
   161  			if err != nil {
   162  				logger.Warnf("Selector.watch() = error{%v}", perrors.WithStack(err))
   163  				listener.Close()
   164  				return err
   165  			}
   166  			logger.Infof("update begin, service event: %v", serviceEvent.String())
   167  			notifyListener.Notify(serviceEvent)
   168  		}
   169  	}
   170  }
   171  
   172  // UnSubscribe returns nil if unsubscribing registry successfully. If not returns an error.
   173  func (pr *polarisRegistry) UnSubscribe(url *common.URL, notifyListener registry.NotifyListener) error {
   174  	// TODO wait polaris support it
   175  	return perrors.New("UnSubscribe not support in polarisRegistry")
   176  }
   177  
   178  // LoadSubscribeInstances load subscribe instance
   179  func (pr *polarisRegistry) LoadSubscribeInstances(url *common.URL, notify registry.NotifyListener) error {
   180  	serviceName := url.Interface()
   181  	resp, err := pr.consumer.GetInstances(&api.GetInstancesRequest{
   182  		GetInstancesRequest: model.GetInstancesRequest{
   183  			Service:   serviceName,
   184  			Namespace: pr.namespace,
   185  		},
   186  	})
   187  	if err != nil {
   188  		return perrors.New(fmt.Sprintf("could not query the instances for serviceName=%s,namespace=%s,error=%v",
   189  			serviceName, pr.namespace, err))
   190  	}
   191  	for i := range resp.Instances {
   192  		if newUrl := generateUrl(resp.Instances[i]); newUrl != nil {
   193  			notify.Notify(&registry.ServiceEvent{Action: remoting.EventTypeAdd, Service: newUrl})
   194  		}
   195  	}
   196  	return nil
   197  }
   198  
   199  // GetURL returns polaris registry's url.
   200  func (pr *polarisRegistry) GetURL() *common.URL {
   201  	return pr.url
   202  }
   203  
   204  func (pr *polarisRegistry) createPolarisWatcher(serviceName string) (*PolarisServiceWatcher, error) {
   205  
   206  	pr.listenerLock.Lock()
   207  	defer pr.listenerLock.Unlock()
   208  
   209  	if _, exist := pr.watchers[serviceName]; !exist {
   210  		subscribeParam := &api.WatchServiceRequest{
   211  			WatchServiceRequest: model.WatchServiceRequest{
   212  				Key: model.ServiceKey{
   213  					Namespace: pr.namespace,
   214  					Service:   serviceName,
   215  				},
   216  			},
   217  		}
   218  
   219  		watcher, err := newPolarisWatcher(subscribeParam, pr.consumer)
   220  		if err != nil {
   221  			return nil, err
   222  		}
   223  		pr.watchers[serviceName] = watcher
   224  	}
   225  
   226  	return pr.watchers[serviceName], nil
   227  }
   228  
   229  // Destroy stop polaris registry.
   230  func (pr *polarisRegistry) Destroy() {
   231  	for i := range pr.registryUrls {
   232  		url := pr.registryUrls[i]
   233  		err := pr.UnRegister(url)
   234  		logger.Infof("DeRegister Polaris URL:%+v", url)
   235  		if err != nil {
   236  			logger.Errorf("Deregister Polaris URL:%+v err:%v", url, err.Error())
   237  		}
   238  	}
   239  	return
   240  }
   241  
   242  // IsAvailable always return true when use polaris
   243  func (pr *polarisRegistry) IsAvailable() bool {
   244  	return true
   245  }
   246  
   247  // createRegisterParam convert dubbo url to polaris instance register request
   248  func createRegisterParam(url *common.URL, serviceName string) *api.InstanceRegisterRequest {
   249  	common.HandleRegisterIPAndPort(url)
   250  	port, _ := strconv.Atoi(url.Port)
   251  
   252  	metadata := make(map[string]string, len(url.GetParams()))
   253  	url.RangeParams(func(key, value string) bool {
   254  		metadata[key] = value
   255  		return true
   256  	})
   257  	metadata[constant.PolarisDubboPath] = url.Path
   258  
   259  	ver := url.GetParam("version", "")
   260  
   261  	req := &api.InstanceRegisterRequest{
   262  		InstanceRegisterRequest: model.InstanceRegisterRequest{
   263  			Service:  serviceName,
   264  			Host:     url.Ip,
   265  			Port:     port,
   266  			Protocol: &url.Protocol,
   267  			Version:  &ver,
   268  			Metadata: metadata,
   269  		},
   270  	}
   271  
   272  	req.SetTTL(defaultHeartbeatIntervalSec)
   273  
   274  	return req
   275  }
   276  
   277  // createDeregisterParam convert dubbo url to polaris instance deregister request
   278  func createDeregisterParam(url *common.URL, serviceName string) *api.InstanceDeRegisterRequest {
   279  	common.HandleRegisterIPAndPort(url)
   280  	port, _ := strconv.Atoi(url.Port)
   281  	return &api.InstanceDeRegisterRequest{
   282  		InstanceDeRegisterRequest: model.InstanceDeRegisterRequest{
   283  			Service: serviceName,
   284  			Host:    url.Ip,
   285  			Port:    port,
   286  		},
   287  	}
   288  }