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 }