github.com/polarismesh/polaris@v1.17.8/apiserver/eurekaserver/applications.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 "encoding/json" 22 "encoding/xml" 23 "fmt" 24 "sort" 25 "strconv" 26 "strings" 27 "sync/atomic" 28 "time" 29 30 apiservice "github.com/polarismesh/specification/source/go/api/v1/service_manage" 31 32 "github.com/polarismesh/polaris/common/model" 33 "github.com/polarismesh/polaris/service" 34 ) 35 36 var ( 37 getCacheServicesFunc = getCacheServices 38 getCacheInstancesFunc = getCacheInstances 39 ) 40 41 // ApplicationsRespCache 全量服务缓存 42 type ApplicationsRespCache struct { 43 AppsResp *ApplicationsResponse 44 Revision string 45 JsonBytes []byte 46 XmlBytes []byte 47 createTimeSec int64 48 } 49 50 // ApplicationsBuilder builder to do application construct 51 type ApplicationsBuilder struct { 52 namingServer service.DiscoverServer 53 namespace string 54 enableSelfPreservation bool 55 // autoincrement version 56 VersionIncrement int64 57 } 58 59 func getCacheServices(namingServer service.DiscoverServer, namespace string) map[string]*model.Service { 60 var newServices = make(map[string]*model.Service) 61 _ = namingServer.Cache().Service().IteratorServices(func(key string, value *model.Service) (bool, error) { 62 if value.Namespace == namespace { 63 newServices[value.Name] = value 64 } 65 return true, nil 66 }) 67 return newServices 68 } 69 70 func getCacheInstances(namingServer service.DiscoverServer, svcId string) ([]*model.Instance, string, error) { 71 var instances []*model.Instance 72 _ = namingServer.Cache().Instance().IteratorInstancesWithService(svcId, 73 func(key string, value *model.Instance) (bool, error) { 74 instances = append(instances, value) 75 return true, nil 76 }) 77 revision, err := namingServer.GetServiceInstanceRevision(svcId, instances) 78 return instances, revision, err 79 } 80 81 // BuildApplications build applications cache with compare to the latest cache 82 func (a *ApplicationsBuilder) BuildApplications(oldAppsCache *ApplicationsRespCache) *ApplicationsRespCache { 83 // 获取所有的服务数据 84 var newServices = getCacheServicesFunc(a.namingServer, a.namespace) 85 var instCount int 86 svcToRevision := make(map[string]string, len(newServices)) 87 svcToToInstances := make(map[string][]*model.Instance) 88 var changed bool 89 for _, newService := range newServices { 90 instances, revision, err := getCacheInstancesFunc(a.namingServer, newService.ID) 91 if err != nil { 92 eurekalog.Errorf("[EurekaServer]fail to get revision for service %s, err is %v", newService.Name, err) 93 continue 94 } 95 // eureka does not return services without instances 96 if len(instances) == 0 { 97 continue 98 } 99 instCount += len(instances) 100 svcName := formatReadName(newService.Name) 101 svcToRevision[svcName] = revision 102 svcToToInstances[svcName] = instances 103 } 104 // 比较并构建Applications缓存 105 hashBuilder := make(map[string]int) 106 newApps := newApplications() 107 var oldApps *Applications 108 if oldAppsCache != nil { 109 oldApps = oldAppsCache.AppsResp.Applications 110 } 111 for svc, instances := range svcToToInstances { 112 var newRevision = svcToRevision[svc] 113 var targetApp *Application 114 if oldApps != nil { 115 oldApp, ok := oldApps.ApplicationMap[svc] 116 if ok && len(oldApp.Revision) > 0 && oldApp.Revision == newRevision { 117 // 没有变化 118 targetApp = oldApp 119 } 120 } 121 if targetApp == nil { 122 // 重新构建 123 targetApp = &Application{ 124 Name: svc, 125 InstanceMap: make(map[string]*InstanceInfo), 126 Revision: newRevision, 127 } 128 a.constructApplication(targetApp, instances) 129 changed = true 130 } 131 statusCount := targetApp.StatusCounts 132 if len(statusCount) > 0 { 133 for status, count := range statusCount { 134 hashBuilder[status] = hashBuilder[status] + count 135 } 136 } 137 if len(targetApp.Instance) > 0 { 138 newApps.Application = append(newApps.Application, targetApp) 139 newApps.ApplicationMap[targetApp.Name] = targetApp 140 } 141 } 142 if oldApps != nil && len(oldApps.Application) != len(newApps.Application) { 143 changed = true 144 } 145 a.buildVersionAndHashCode(changed, hashBuilder, newApps) 146 return constructResponseCache(newApps, instCount, false) 147 } 148 149 func (a *ApplicationsBuilder) constructApplication(app *Application, instances []*model.Instance) { 150 if len(instances) == 0 { 151 return 152 } 153 app.StatusCounts = make(map[string]int) 154 155 // 转换时候要区分2种情况,一种是从eureka注册上来的,一种不是 156 for _, instance := range instances { 157 if !instance.Healthy() { 158 continue 159 } 160 instanceInfo := buildInstance(app.Name, instance.Proto, instance.ModifyTime.UnixNano()/1e6) 161 status := instanceInfo.Status 162 app.StatusCounts[status] = app.StatusCounts[status] + 1 163 app.Instance = append(app.Instance, instanceInfo) 164 app.InstanceMap[instanceInfo.InstanceId] = instanceInfo 165 } 166 if nil == app.Instance { 167 app.Instance = []*InstanceInfo{} 168 } 169 } 170 171 func (a *ApplicationsBuilder) buildVersionAndHashCode(changed bool, hashBuilder map[string]int, newApps *Applications) { 172 var nextVersion int64 173 if changed { 174 nextVersion = atomic.AddInt64(&a.VersionIncrement, 1) 175 } else { 176 nextVersion = atomic.LoadInt64(&a.VersionIncrement) 177 } 178 buildHashCode(strconv.Itoa(int(nextVersion)), hashBuilder, newApps) 179 } 180 181 func buildHashCode(version string, hashBuilder map[string]int, newApps *Applications) { 182 // 构建hashValue 183 newApps.AppsHashCode = buildHashStr(hashBuilder) 184 newApps.VersionsDelta = version 185 } 186 187 func parseStatus(instance *apiservice.Instance) string { 188 if instance.GetIsolate().GetValue() { 189 status := instance.Metadata[InternalMetadataStatus] 190 switch status { 191 case StatusDown: 192 return StatusDown 193 default: 194 return StatusOutOfService 195 } 196 } 197 return StatusUp 198 } 199 200 func parsePortWrapper(info *InstanceInfo, instance *apiservice.Instance) { 201 metadata := instance.GetMetadata() 202 var securePortOk bool 203 var securePortEnabledOk bool 204 var securePort string 205 var securePortEnabled string 206 var insecurePortOk bool 207 var insecurePortEnabledOk bool 208 var insecurePort string 209 var insecurePortEnabled string 210 if len(metadata) > 0 { 211 securePort, securePortOk = instance.GetMetadata()[MetadataSecurePort] 212 securePortEnabled, securePortEnabledOk = instance.GetMetadata()[MetadataSecurePortEnabled] 213 insecurePort, insecurePortOk = instance.GetMetadata()[MetadataInsecurePort] 214 insecurePortEnabled, insecurePortEnabledOk = instance.GetMetadata()[MetadataInsecurePortEnabled] 215 } 216 if securePortOk && securePortEnabledOk && insecurePortOk && insecurePortEnabledOk { 217 // if metadata contains all port/securePort,port.enabled/securePort.enabled 218 sePort, err := strconv.Atoi(securePort) 219 if err != nil { 220 sePort = 0 221 eurekalog.Errorf("[EUREKA_SERVER]parse secure port error: %+v", err) 222 } 223 sePortEnabled, err := strconv.ParseBool(securePortEnabled) 224 if err != nil { 225 sePortEnabled = false 226 eurekalog.Errorf("[EUREKA_SERVER]parse secure port enabled error: %+v", err) 227 } 228 229 info.SecurePort.Port = sePort 230 info.SecurePort.Enabled = sePortEnabled 231 232 insePort, err := strconv.Atoi(insecurePort) 233 if err != nil { 234 insePort = 0 235 eurekalog.Errorf("[EUREKA_SERVER]parse insecure port error: %+v", err) 236 } 237 insePortEnabled, err := strconv.ParseBool(insecurePortEnabled) 238 if err != nil { 239 insePortEnabled = false 240 eurekalog.Errorf("[EUREKA_SERVER]parse insecure port enabled error: %+v", err) 241 } 242 243 info.Port.Port = insePort 244 info.Port.Enabled = insePortEnabled 245 } else { 246 protocol := instance.GetProtocol().GetValue() 247 port := instance.GetPort().GetValue() 248 if protocol == SecureProtocol { 249 info.SecurePort.Port = int(port) 250 info.SecurePort.Enabled = "true" 251 if len(metadata) > 0 { 252 if insecurePortStr, ok := metadata[MetadataInsecurePort]; ok { 253 insecurePort, _ := strconv.Atoi(insecurePortStr) 254 if insecurePort > 0 { 255 info.Port.Port = insecurePort 256 info.Port.Enabled = "true" 257 } 258 } 259 } 260 } else { 261 info.Port.Port = int(port) 262 info.Port.Enabled = "true" 263 } 264 } 265 } 266 267 func parseLeaseInfo(leaseInfo *LeaseInfo, instance *apiservice.Instance) { 268 var ( 269 metadata = instance.GetMetadata() 270 durationInSec int 271 renewIntervalSec int 272 ) 273 if metadata != nil { 274 durationInSecStr, ok := metadata[MetadataDuration] 275 if ok { 276 durationInSec, _ = strconv.Atoi(durationInSecStr) 277 } 278 renewIntervalStr, ok := metadata[MetadataRenewalInterval] 279 if ok { 280 renewIntervalSec, _ = strconv.Atoi(renewIntervalStr) 281 } 282 } 283 if durationInSec > 0 { 284 leaseInfo.DurationInSecs = durationInSec 285 } 286 if renewIntervalSec > 0 { 287 leaseInfo.RenewalIntervalInSecs = renewIntervalSec 288 } 289 } 290 291 func buildInstance(appName string, instance *apiservice.Instance, lastModifyTime int64) *InstanceInfo { 292 instanceInfo := &InstanceInfo{ 293 CountryId: DefaultCountryIdInt, 294 Port: &PortWrapper{ 295 Enabled: "false", 296 Port: DefaultInsecurePort, 297 }, 298 SecurePort: &PortWrapper{ 299 Enabled: "false", 300 Port: DefaultSSLPort, 301 }, 302 LeaseInfo: &LeaseInfo{ 303 RenewalIntervalInSecs: DefaultRenewInterval, 304 DurationInSecs: DefaultDuration, 305 }, 306 Metadata: &Metadata{ 307 Meta: make(map[string]interface{}), 308 }, 309 RealInstance: instance, 310 } 311 instanceInfo.AppName = appName 312 // 属于eureka注册的实例 313 instanceInfo.InstanceId = instance.GetId().GetValue() 314 metadata := instance.GetMetadata() 315 if metadata == nil { 316 metadata = map[string]string{} 317 } 318 if eurekaInstanceId, ok := metadata[MetadataInstanceId]; ok { 319 instanceInfo.InstanceId = eurekaInstanceId 320 } 321 if hostName, ok := metadata[MetadataHostName]; ok { 322 instanceInfo.HostName = hostName 323 } 324 instanceInfo.IpAddr = instance.GetHost().GetValue() 325 instanceInfo.Status = parseStatus(instance) 326 instanceInfo.OverriddenStatus = StatusUnknown 327 parsePortWrapper(instanceInfo, instance) 328 if countryIdStr, ok := metadata[MetadataCountryId]; ok { 329 cId, err := strconv.Atoi(countryIdStr) 330 if err == nil { 331 instanceInfo.CountryId = cId 332 } 333 } 334 dciClazz, ok1 := metadata[MetadataDataCenterInfoClazz] 335 dciName, ok2 := metadata[MetadataDataCenterInfoName] 336 if ok1 && ok2 { 337 instanceInfo.DataCenterInfo = &DataCenterInfo{ 338 Clazz: dciClazz, 339 Name: dciName, 340 } 341 } else { 342 instanceInfo.DataCenterInfo = buildDataCenterInfo() 343 } 344 parseLeaseInfo(instanceInfo.LeaseInfo, instance) 345 for metaKey, metaValue := range metadata { 346 if strings.HasPrefix(metaKey, "internal-") { 347 continue 348 } 349 instanceInfo.Metadata.Meta[metaKey] = metaValue 350 } 351 if url, ok := metadata[MetadataHomePageUrl]; ok { 352 instanceInfo.HomePageUrl = url 353 } 354 if url, ok := metadata[MetadataStatusPageUrl]; ok { 355 instanceInfo.StatusPageUrl = url 356 } 357 if url, ok := metadata[MetadataHealthCheckUrl]; ok { 358 instanceInfo.HealthCheckUrl = url 359 } 360 if address, ok := metadata[MetadataVipAddress]; ok { 361 instanceInfo.VipAddress = address 362 } 363 if address, ok := metadata[MetadataSecureVipAddress]; ok { 364 instanceInfo.SecureVipAddress = address 365 } 366 if instanceInfo.VipAddress == "" { 367 instanceInfo.VipAddress = appName 368 } 369 if instanceInfo.HostName == "" { 370 instanceInfo.HostName = instance.GetHost().GetValue() 371 } 372 buildLocationInfo(instanceInfo, instance) 373 instanceInfo.LastUpdatedTimestamp = strconv.Itoa(int(lastModifyTime)) 374 instanceInfo.ActionType = ActionAdded 375 return instanceInfo 376 } 377 378 func buildDataCenterInfo() *DataCenterInfo { 379 customDciClass, ok1 := CustomEurekaParameters[CustomKeyDciClass] 380 customDciName, ok2 := CustomEurekaParameters[CustomKeyDciName] 381 if ok1 && ok2 { 382 return &DataCenterInfo{ 383 Clazz: customDciClass, 384 Name: customDciName, 385 } 386 } else if ok1 && !ok2 { 387 return &DataCenterInfo{ 388 Clazz: customDciClass, 389 Name: DefaultDciName, 390 } 391 } else if !ok1 && ok2 { 392 return &DataCenterInfo{ 393 Clazz: DefaultDciClazz, 394 Name: customDciName, 395 } 396 } else { 397 return DefaultDataCenterInfo 398 } 399 } 400 401 func buildLocationInfo(instanceInfo *InstanceInfo, instance *apiservice.Instance) { 402 var region string 403 var zone string 404 var campus string 405 if location := instance.GetLocation(); location != nil { 406 region = location.GetRegion().GetValue() 407 zone = location.GetZone().GetValue() 408 campus = location.GetCampus().GetValue() 409 } 410 if _, ok := instanceInfo.Metadata.Meta[KeyRegion]; !ok && len(region) > 0 { 411 instanceInfo.Metadata.Meta[KeyRegion] = region 412 } 413 if _, ok := instanceInfo.Metadata.Meta[keyZone]; !ok && len(zone) > 0 { 414 instanceInfo.Metadata.Meta[keyZone] = zone 415 } 416 if _, ok := instanceInfo.Metadata.Meta[keyCampus]; !ok && len(campus) > 0 { 417 instanceInfo.Metadata.Meta[keyCampus] = campus 418 } 419 } 420 421 func newApplications() *Applications { 422 return &Applications{ 423 ApplicationMap: make(map[string]*Application), 424 Application: make([]*Application, 0), 425 } 426 } 427 428 func constructResponseCache(newApps *Applications, instCount int, delta bool) *ApplicationsRespCache { 429 appsHashCode := newApps.AppsHashCode 430 newAppsCache := &ApplicationsRespCache{ 431 AppsResp: &ApplicationsResponse{Applications: newApps}, 432 createTimeSec: time.Now().Unix(), 433 } 434 // 预先做一次序列化,以免高并发时候序列化会使得内存峰值过高 435 jsonBytes, err := json.MarshalIndent(newAppsCache.AppsResp, "", " ") 436 if err != nil { 437 eurekalog.Errorf("[EUREKA_SERVER]fail to marshal apps %s to json, err is %v", appsHashCode, err) 438 } else { 439 newAppsCache.JsonBytes = jsonBytes 440 } 441 xmlBytes, err := xml.MarshalIndent(newAppsCache.AppsResp.Applications, " ", " ") 442 if err != nil { 443 eurekalog.Errorf("[EUREKA_SERVER]fail to marshal apps %s to xml, err is %v", appsHashCode, err) 444 } else { 445 newAppsCache.XmlBytes = xmlBytes 446 } 447 if !delta && len(jsonBytes) > 0 { 448 newAppsCache.Revision = sha1s(jsonBytes) 449 } 450 eurekalog.Infof("[EUREKA_SERVER]success to build apps cache, delta is %v, "+ 451 "length xmlBytes is %d, length jsonBytes is %d, instCount is %d", delta, len(xmlBytes), len(jsonBytes), instCount) 452 return newAppsCache 453 } 454 455 func buildHashStr(counts map[string]int) string { 456 if len(counts) == 0 { 457 return "" 458 } 459 slice := make([]string, 0, len(counts)) 460 for k := range counts { 461 slice = append(slice, k) 462 } 463 sort.Strings(slice) 464 builder := &strings.Builder{} 465 for _, status := range slice { 466 builder.WriteString(fmt.Sprintf("%s_%d_", status, counts[status])) 467 } 468 return builder.String() 469 }