github.com/polarismesh/polaris@v1.17.8/apiserver/eurekaserver/write.go (about)

     1  /**
     2   * Tencent is pleased to support the open source community by making Polaris available.
     3   *
     4   * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
     5   *
     6   * Licensed under the BSD 3-Clause License (the "License");
     7   * you may not use this file except in compliance with the License.
     8   * You may obtain a copy of the License at
     9   *
    10   * https://opensource.org/licenses/BSD-3-Clause
    11   *
    12   * Unless required by applicable law or agreed to in writing, software distributed
    13   * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
    14   * CONDITIONS OF ANY KIND, either express or implied. See the License for the
    15   * specific language governing permissions and limitations under the License.
    16   */
    17  
    18  package eurekaserver
    19  
    20  import (
    21  	"context"
    22  	"math"
    23  	"strconv"
    24  	"strings"
    25  
    26  	"github.com/golang/protobuf/ptypes/wrappers"
    27  	apimodel "github.com/polarismesh/specification/source/go/api/v1/model"
    28  	apiservice "github.com/polarismesh/specification/source/go/api/v1/service_manage"
    29  	"go.uber.org/zap"
    30  
    31  	api "github.com/polarismesh/polaris/common/api/v1"
    32  	"github.com/polarismesh/polaris/common/model"
    33  	commonstore "github.com/polarismesh/polaris/common/store"
    34  	"github.com/polarismesh/polaris/common/utils"
    35  )
    36  
    37  func checkOrBuildNewInstanceId(appId string, instId string, generateUniqueInstId bool) string {
    38  	if !generateUniqueInstId {
    39  		return instId
    40  	}
    41  	lowerAppId := strings.ToLower(appId)
    42  	lowerInstIdId := strings.ToLower(instId)
    43  	if strings.Contains(lowerInstIdId, lowerAppId) {
    44  		return instId
    45  	}
    46  	return lowerAppId + ":" + lowerInstIdId
    47  }
    48  
    49  func checkOrBuildNewInstanceIdByNamespace(namespace string, defaultNamespace string, appId string,
    50  	instId string, generateUniqueInstId bool) string {
    51  	instId = checkOrBuildNewInstanceId(appId, instId, generateUniqueInstId)
    52  	if namespace != defaultNamespace {
    53  		return namespace + ":" + instId
    54  	}
    55  	return instId
    56  }
    57  
    58  func buildBaseInstance(
    59  	instance *InstanceInfo, namespace string, defaultNamespace string,
    60  	appId string, generateUniqueInstId bool) *apiservice.Instance {
    61  	targetInstance := &apiservice.Instance{}
    62  	eurekaMetadata := make(map[string]string)
    63  
    64  	eurekaMetadata[MetadataRegisterFrom] = ServerEureka
    65  	eurekaInstanceId := instance.InstanceId
    66  	if len(eurekaInstanceId) == 0 {
    67  		eurekaInstanceId = instance.HostName
    68  	}
    69  	eurekaMetadata[MetadataInstanceId] = eurekaInstanceId
    70  	if len(instance.AppGroupName) > 0 {
    71  		eurekaMetadata[MetadataAppGroupName] = instance.AppGroupName
    72  	}
    73  	countryIdStr := ObjectToString(instance.CountryId)
    74  	if DefaultCountryId != countryIdStr {
    75  		eurekaMetadata[MetadataCountryId] = countryIdStr
    76  	}
    77  	if instance.DataCenterInfo != nil {
    78  		if DefaultDciClazz != instance.DataCenterInfo.Clazz {
    79  			eurekaMetadata[MetadataDataCenterInfoClazz] = instance.DataCenterInfo.Clazz
    80  		}
    81  		if DefaultDciName != instance.DataCenterInfo.Name {
    82  			eurekaMetadata[MetadataDataCenterInfoName] = instance.DataCenterInfo.Name
    83  		}
    84  	}
    85  	if len(instance.HostName) > 0 {
    86  		eurekaMetadata[MetadataHostName] = instance.HostName
    87  	}
    88  	if len(instance.HomePageUrl) > 0 {
    89  		eurekaMetadata[MetadataHomePageUrl] = instance.HomePageUrl
    90  	}
    91  	if len(instance.StatusPageUrl) > 0 {
    92  		eurekaMetadata[MetadataStatusPageUrl] = instance.StatusPageUrl
    93  	}
    94  	if len(instance.HealthCheckUrl) > 0 {
    95  		eurekaMetadata[MetadataHealthCheckUrl] = instance.HealthCheckUrl
    96  	}
    97  	if len(instance.VipAddress) > 0 {
    98  		eurekaMetadata[MetadataVipAddress] = instance.VipAddress
    99  	}
   100  	if len(instance.SecureVipAddress) > 0 {
   101  		eurekaMetadata[MetadataSecureVipAddress] = instance.SecureVipAddress
   102  	}
   103  	targetInstance.Id = &wrappers.StringValue{
   104  		Value: checkOrBuildNewInstanceIdByNamespace(namespace, defaultNamespace,
   105  			appId, eurekaInstanceId, generateUniqueInstId),
   106  	}
   107  	targetInstance.Metadata = eurekaMetadata
   108  	targetInstance.Service = &wrappers.StringValue{Value: appId}
   109  	targetInstance.Namespace = &wrappers.StringValue{Value: namespace}
   110  	targetInstance.Host = &wrappers.StringValue{Value: instance.IpAddr}
   111  	if instance.Metadata != nil && len(instance.Metadata.Meta) > 0 {
   112  		targetInstance.Location = &apimodel.Location{}
   113  		for k, v := range instance.Metadata.Meta {
   114  			strValue := ObjectToString(v)
   115  			switch k {
   116  			case KeyRegion:
   117  				targetInstance.Location.Region = &wrappers.StringValue{Value: strValue}
   118  			case keyZone:
   119  				targetInstance.Location.Zone = &wrappers.StringValue{Value: strValue}
   120  			case keyCampus:
   121  				targetInstance.Location.Campus = &wrappers.StringValue{Value: strValue}
   122  			}
   123  			targetInstance.Metadata[k] = strValue
   124  		}
   125  	}
   126  	targetInstance.Weight = &wrappers.UInt32Value{Value: 100}
   127  	buildHealthCheck(instance, targetInstance, eurekaMetadata)
   128  	buildStatus(instance, targetInstance)
   129  	return targetInstance
   130  }
   131  
   132  func buildHealthCheck(instance *InstanceInfo, targetInstance *apiservice.Instance, eurekaMetadata map[string]string) {
   133  	leaseInfo := instance.LeaseInfo
   134  	durationInSecs := DefaultDuration
   135  	renewalIntervalInSecs := DefaultRenewInterval
   136  	if leaseInfo != nil {
   137  		if leaseInfo.RenewalIntervalInSecs != 0 {
   138  			renewalIntervalInSecs = leaseInfo.RenewalIntervalInSecs
   139  		}
   140  		if leaseInfo.DurationInSecs != 0 {
   141  			durationInSecs = leaseInfo.DurationInSecs
   142  		}
   143  	}
   144  	eurekaMetadata[MetadataRenewalInterval] = strconv.Itoa(renewalIntervalInSecs)
   145  	eurekaMetadata[MetadataDuration] = strconv.Itoa(durationInSecs)
   146  	durationMin := math.Ceil(float64(durationInSecs) / 3)
   147  	ttl := uint32(math.Min(durationMin, float64(renewalIntervalInSecs)))
   148  
   149  	targetInstance.EnableHealthCheck = &wrappers.BoolValue{Value: true}
   150  	targetInstance.HealthCheck = &apiservice.HealthCheck{
   151  		Type:      apiservice.HealthCheck_HEARTBEAT,
   152  		Heartbeat: &apiservice.HeartbeatHealthCheck{Ttl: &wrappers.UInt32Value{Value: ttl}},
   153  	}
   154  }
   155  
   156  func buildStatus(instance *InstanceInfo, targetInstance *apiservice.Instance) {
   157  	// eureka注册的实例默认healthy为true,即使设置为false也会被心跳触发变更为true
   158  	// eureka实例非UP状态设置isolate为true,进行流量隔离
   159  	targetInstance.Healthy = &wrappers.BoolValue{Value: true}
   160  	targetInstance.Isolate = &wrappers.BoolValue{Value: false}
   161  	if instance.Status != "UP" {
   162  		targetInstance.Isolate = &wrappers.BoolValue{Value: true}
   163  	}
   164  }
   165  
   166  func convertEurekaInstance(
   167  	instance *InstanceInfo, namespace string, defaultNamespace string,
   168  	appId string, generateUniqueInstId bool) *apiservice.Instance {
   169  	var secureEnable bool
   170  	var securePort int
   171  	var insecureEnable bool
   172  	var insecurePort int
   173  
   174  	securePortWrap := instance.SecurePort
   175  	if securePortWrap != nil {
   176  		secureEnable = securePortWrap.RealEnable
   177  		securePort = securePortWrap.RealPort
   178  	} else {
   179  		secureEnable = false
   180  		securePort = DefaultSSLPort
   181  	}
   182  	insecurePortWrap := instance.Port
   183  	if insecurePortWrap != nil {
   184  		insecureEnable = insecurePortWrap.RealEnable
   185  		insecurePort = insecurePortWrap.RealPort
   186  	} else {
   187  		insecureEnable = true
   188  		insecurePort = DefaultInsecurePort
   189  	}
   190  
   191  	targetInstance := buildBaseInstance(instance, namespace, defaultNamespace, appId, generateUniqueInstId)
   192  
   193  	// 同时打开2个端口,通过medata保存http端口
   194  	targetInstance.Protocol = &wrappers.StringValue{Value: InsecureProtocol}
   195  	targetInstance.Port = &wrappers.UInt32Value{Value: uint32(insecurePort)}
   196  	targetInstance.Metadata[MetadataInsecurePort] = strconv.Itoa(insecurePort)
   197  	targetInstance.Metadata[MetadataInsecurePortEnabled] = strconv.FormatBool(insecureEnable)
   198  	targetInstance.Metadata[MetadataSecurePort] = strconv.Itoa(securePort)
   199  	targetInstance.Metadata[MetadataSecurePortEnabled] = strconv.FormatBool(secureEnable)
   200  	// 保存客户端注册时设置的 status 信息,该信息不会随着心跳的变化而调整
   201  	targetInstance.Metadata[InternalMetadataStatus] = instance.Status
   202  	targetInstance.Metadata[InternalMetadataOverriddenStatus] = instance.OverriddenStatus
   203  	return targetInstance
   204  }
   205  
   206  func (h *EurekaServer) registerInstances(
   207  	ctx context.Context, namespace string, appId string, instance *InstanceInfo, replicated bool) uint32 {
   208  	ctx = context.WithValue(
   209  		ctx, model.CtxEventKeyMetadata, map[string]string{MetadataReplicate: strconv.FormatBool(replicated)})
   210  	ctx = context.WithValue(ctx, utils.ContextOpenAsyncRegis, h.allowAsyncRegis)
   211  	appId = formatWriteName(appId)
   212  	// 1. 先转换数据结构
   213  	totalInstance := convertEurekaInstance(instance, namespace, h.namespace, appId, h.generateUniqueInstId)
   214  	// 3. 注册实例
   215  	resp := h.namingServer.RegisterInstance(ctx, totalInstance)
   216  	// 4. 注册成功,则返回
   217  	if resp.GetCode().GetValue() == api.ExecuteSuccess || resp.GetCode().GetValue() == api.ExistedResource {
   218  		return api.ExecuteSuccess
   219  	}
   220  	// 5. 如果报服务不存在,对服务进行注册
   221  	if resp.Code.Value == api.NotFoundResource {
   222  		svc := &apiservice.Service{}
   223  		svc.Namespace = &wrappers.StringValue{Value: namespace}
   224  		svc.Name = &wrappers.StringValue{Value: appId}
   225  		svcResp := h.namingServer.CreateServices(ctx, []*apiservice.Service{svc})
   226  		svcCreateCode := svcResp.GetCode().GetValue()
   227  		if svcCreateCode != api.ExecuteSuccess && svcCreateCode != api.ExistedResource {
   228  			return svcCreateCode
   229  		}
   230  		// 6. 再重试注册实例列表
   231  		resp = h.namingServer.RegisterInstance(ctx, totalInstance)
   232  		return resp.GetCode().GetValue()
   233  	}
   234  	return resp.GetCode().GetValue()
   235  }
   236  
   237  func (h *EurekaServer) deregisterInstance(
   238  	ctx context.Context, namespace string, appId string, instanceId string, replicated bool) uint32 {
   239  	ctx = context.WithValue(
   240  		ctx, model.CtxEventKeyMetadata, map[string]string{
   241  			MetadataReplicate:  strconv.FormatBool(replicated),
   242  			MetadataInstanceId: instanceId,
   243  		})
   244  	ctx = context.WithValue(ctx, utils.ContextOpenAsyncRegis, true)
   245  	instanceId = checkOrBuildNewInstanceIdByNamespace(namespace, h.namespace, appId, instanceId, h.generateUniqueInstId)
   246  	resp := h.namingServer.DeregisterInstance(ctx, &apiservice.Instance{Id: &wrappers.StringValue{Value: instanceId}})
   247  	return resp.GetCode().GetValue()
   248  }
   249  
   250  func (h *EurekaServer) updateStatus(
   251  	ctx context.Context, namespace string, appId string, instanceId string, status string, replicated bool) uint32 {
   252  	ctx = context.WithValue(
   253  		ctx, model.CtxEventKeyMetadata, map[string]string{
   254  			MetadataReplicate:  strconv.FormatBool(replicated),
   255  			MetadataInstanceId: instanceId,
   256  		})
   257  	instanceId = checkOrBuildNewInstanceIdByNamespace(namespace, h.namespace, appId, instanceId, h.generateUniqueInstId)
   258  
   259  	saveIns, err := h.originDiscoverSvr.Cache().GetStore().GetInstance(instanceId)
   260  	if err != nil {
   261  		eurekalog.Error("[EUREKA-SERVER] get instance from store when update status", zap.Error(err))
   262  		return uint32(commonstore.StoreCode2APICode(err))
   263  	}
   264  	if saveIns == nil {
   265  		return uint32(apimodel.Code_NotFoundInstance)
   266  	}
   267  
   268  	metadata := saveIns.Metadata()
   269  	metadata[InternalMetadataStatus] = status
   270  	isolated := status != StatusUp
   271  
   272  	updateIns := &apiservice.Instance{
   273  		Id:       &wrappers.StringValue{Value: instanceId},
   274  		Isolate:  &wrappers.BoolValue{Value: isolated},
   275  		Metadata: metadata,
   276  	}
   277  
   278  	resp := h.namingServer.UpdateInstance(ctx, updateIns)
   279  	return resp.GetCode().GetValue()
   280  }
   281  
   282  func (h *EurekaServer) renew(ctx context.Context, namespace string, appId string,
   283  	instanceId string, replicated bool) uint32 {
   284  	ctx = context.WithValue(
   285  		ctx, model.CtxEventKeyMetadata, map[string]string{
   286  			MetadataReplicate:  strconv.FormatBool(replicated),
   287  			MetadataInstanceId: instanceId,
   288  		})
   289  	instanceId = checkOrBuildNewInstanceIdByNamespace(namespace, h.namespace, appId, instanceId, h.generateUniqueInstId)
   290  	resp := h.healthCheckServer.Report(ctx, &apiservice.Instance{Id: &wrappers.StringValue{Value: instanceId}})
   291  	code := resp.GetCode().GetValue()
   292  
   293  	// 如果目标实例存在,但是没有开启心跳,对于 eureka 来说,仍然属于心跳上报成功
   294  	if code == api.HeartbeatOnDisabledIns {
   295  		return api.ExecuteSuccess
   296  	}
   297  	return code
   298  }
   299  
   300  func (h *EurekaServer) updateMetadata(
   301  	ctx context.Context, namespace string, appId string, instanceId string, metadata map[string]string) uint32 {
   302  	metadata[MetadataInstanceId] = instanceId
   303  	instanceId = checkOrBuildNewInstanceIdByNamespace(namespace, h.namespace, appId, instanceId, h.generateUniqueInstId)
   304  	resp := h.namingServer.UpdateInstance(ctx,
   305  		&apiservice.Instance{Id: &wrappers.StringValue{Value: instanceId}, Metadata: metadata})
   306  	return resp.GetCode().GetValue()
   307  }