github.com/polarismesh/polaris@v1.17.8/apiserver/eurekaserver/server.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 "errors" 23 "fmt" 24 "net" 25 "net/http" 26 "strconv" 27 "strings" 28 "time" 29 30 "github.com/emicklei/go-restful/v3" 31 "go.uber.org/zap" 32 33 "github.com/polarismesh/polaris/apiserver" 34 "github.com/polarismesh/polaris/common/conn/keepalive" 35 connlimit "github.com/polarismesh/polaris/common/conn/limit" 36 "github.com/polarismesh/polaris/common/eventhub" 37 "github.com/polarismesh/polaris/common/metrics" 38 "github.com/polarismesh/polaris/common/secure" 39 "github.com/polarismesh/polaris/common/utils" 40 "github.com/polarismesh/polaris/plugin" 41 "github.com/polarismesh/polaris/service" 42 "github.com/polarismesh/polaris/service/healthcheck" 43 ) 44 45 const ( 46 SecureProtocol = "HTTPS" 47 InsecureProtocol = "HTTP" 48 49 MetadataRegisterFrom = "internal-register-from" 50 MetadataAppGroupName = "internal-eureka-app-group" 51 MetadataCountryId = "internal-eureka-country-id" 52 MetadataDataCenterInfoClazz = "internal-eureka-dci-clazz" 53 MetadataDataCenterInfoName = "internal-eureka-dci-name" 54 MetadataHostName = "internal-eureka-hostname" 55 MetadataRenewalInterval = "internal-eureka-renewal-interval" 56 MetadataDuration = "internal-eureka-duration" 57 MetadataHomePageUrl = "internal-eureka-home-url" 58 MetadataStatusPageUrl = "internal-eureka-status-url" 59 MetadataHealthCheckUrl = "internal-eureka-health-url" 60 MetadataVipAddress = "internal-eureka-vip" 61 MetadataSecureVipAddress = "internal-eureka-secure-vip" 62 MetadataInsecurePort = "internal-eureka-insecure-port" 63 MetadataInsecurePortEnabled = "internal-eureka-insecure-port-enabled" 64 MetadataSecurePort = "internal-eureka-secure-port" 65 MetadataSecurePortEnabled = "internal-eureka-secure-port-enabled" 66 MetadataReplicate = "internal-eureka-replicate" 67 MetadataInstanceId = "internal-eureka-instance-id" 68 69 InternalMetadataStatus = "internal-eureka-status" 70 InternalMetadataOverriddenStatus = "internal-eureka-overriddenStatus" 71 72 ServerEureka = "eureka" 73 74 KeyRegion = "region" 75 keyZone = "zone" 76 keyCampus = "campus" 77 78 StatusOutOfService = "OUT_OF_SERVICE" 79 StatusUp = "UP" 80 StatusDown = "DOWN" 81 StatusUnknown = "UNKNOWN" 82 83 ActionAdded = "ADDED" 84 ActionModified = "MODIFIED" 85 ActionDeleted = "DELETED" 86 87 DefaultCountryIdInt = 1 88 DefaultDciClazz = "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo" 89 DefaultDciName = "MyOwn" 90 DefaultRenewInterval = 30 91 DefaultDuration = 90 92 DefaultUnhealthyExpireInterval = 180 93 94 DefaultOwner = "polaris" 95 DefaultSSLPort = 443 96 DefaultInsecurePort = 8080 97 98 operationRegister = "POST:/eureka/apps/{application}" 99 operationDeregister = "DELETE:/eureka/apps/{application}/{instanceId}" 100 operationHeartbeat = "PUT:/eureka/apps/{application}/{instanceId}" 101 operationAllInstances = "GET:/eureka/apps" 102 operationDelta = "GET:/eureka/apps/delta" 103 operationAllAppIDInstances = "GET:/eureka/apps/{application}" 104 operationAppIDInstance = "GET:/eureka/apps/{application}/{instanceId}" 105 operationStatusChange = "PUT:/eureka/apps/{application}/{instanceId}/status" 106 operationDeleteStatusChange = "DELETE:/eureka/apps/{application}/{instanceId}/status" 107 108 pathPrefix = "/eureka/apps" 109 statusSuffix = "/status" 110 111 statusCodeHeader = utils.PolarisCode 112 113 CustomKeyDciClass = "dataCenterInfoClass" 114 CustomKeyDciName = "dataCenterInfoName" 115 ) 116 117 var ( 118 DefaultDataCenterInfo = &DataCenterInfo{ 119 Clazz: DefaultDciClazz, 120 Name: DefaultDciName, 121 } 122 DefaultCountryId = strconv.Itoa(DefaultCountryIdInt) 123 124 CustomEurekaParameters = make(map[string]string) 125 ) 126 127 // EurekaServer is the Eureka server 128 type EurekaServer struct { 129 server *http.Server 130 namingServer service.DiscoverServer 131 originDiscoverSvr service.DiscoverServer 132 healthCheckServer *healthcheck.Server 133 connLimitConfig *connlimit.Config 134 tlsInfo *secure.TLSInfo 135 option map[string]interface{} 136 openAPI map[string]apiserver.APIConfig 137 workers *ApplicationsWorkers 138 listenPort uint32 139 listenIP string 140 exitCh chan struct{} 141 start bool 142 restart bool 143 rateLimit plugin.Ratelimit 144 statis plugin.Statis 145 namespace string 146 refreshInterval time.Duration 147 deltaExpireInterval time.Duration 148 enableSelfPreservation bool 149 replicateWorkers *ReplicateWorkers 150 eventHandlerHandler *EurekaInstanceEventHandler 151 152 replicatePeers map[string][]string 153 generateUniqueInstId bool 154 subCtxs []*eventhub.SubscribtionContext 155 156 allowAsyncRegis bool 157 } 158 159 // GetPort 获取端口 160 func (h *EurekaServer) GetPort() uint32 { 161 return h.listenPort 162 } 163 164 // GetProtocol 获取协议 165 func (h *EurekaServer) GetProtocol() string { 166 return ServerEureka 167 } 168 169 // Initialize 初始化HTTP API服务器 170 func (h *EurekaServer) Initialize(ctx context.Context, option map[string]interface{}, 171 api map[string]apiserver.APIConfig) error { 172 if ipValue, ok := option[optionListenIP]; ok { 173 h.listenIP = ipValue.(string) 174 } else { 175 h.listenIP = DefaultListenIP 176 } 177 if portValue, ok := option[optionListenPort]; ok { 178 h.listenPort = uint32(portValue.(int)) 179 } else { 180 h.listenPort = uint32(DefaultListenPort) 181 } 182 h.option = option 183 h.openAPI = api 184 h.subCtxs = make([]*eventhub.SubscribtionContext, 0, 4) 185 h.allowAsyncRegis = true 186 187 var namespace = DefaultNamespace 188 if namespaceValue, ok := option[optionNamespace]; ok { 189 theNamespace := namespaceValue.(string) 190 if len(theNamespace) > 0 { 191 namespace = theNamespace 192 } 193 } 194 h.namespace = namespace 195 196 if replicatePeersValue, ok := option[optionPeerNodesToReplicate]; ok { 197 replicatePeerObjs := replicatePeersValue.([]interface{}) 198 h.replicatePeers = parsePeersToReplicate(h.namespace, replicatePeerObjs) 199 if len(h.replicatePeers) > 0 { 200 h.replicateWorkers = NewReplicateWorkers(ctx, h.replicatePeers) 201 } 202 } 203 204 var refreshInterval int 205 if value, ok := option[optionRefreshInterval]; ok { 206 refreshInterval = value.(int) 207 } 208 if refreshInterval <= 0 { 209 refreshInterval = DefaultRefreshInterval 210 } 211 212 var deltaExpireInterval int 213 if value, ok := option[optionDeltaExpireInterval]; ok { 214 deltaExpireInterval = value.(int) 215 } 216 if deltaExpireInterval <= 0 { 217 deltaExpireInterval = DefaultDetailExpireInterval 218 } 219 220 // 连接数限制的配置 221 if raw, _ := option[optionConnLimit].(map[interface{}]interface{}); raw != nil { 222 connLimitConfig, err := connlimit.ParseConnLimitConfig(raw) 223 if err != nil { 224 return err 225 } 226 h.connLimitConfig = connLimitConfig 227 } 228 if raw, _ := option[optionTLS].(map[interface{}]interface{}); raw != nil { 229 tlsConfig, err := secure.ParseTLSConfig(raw) 230 if err != nil { 231 return err 232 } 233 h.tlsInfo = &secure.TLSInfo{ 234 CertFile: tlsConfig.CertFile, 235 KeyFile: tlsConfig.KeyFile, 236 TrustedCAFile: tlsConfig.TrustedCAFile, 237 } 238 } 239 240 h.refreshInterval = time.Duration(refreshInterval) * time.Second 241 h.deltaExpireInterval = time.Duration(deltaExpireInterval) * time.Second 242 243 var enableSelfPreservation bool 244 if value, ok := option[optionEnableSelfPreservation]; ok { 245 enableSelfPreservation = value.(bool) 246 } else { 247 enableSelfPreservation = DefaultEnableSelfPreservation 248 } 249 h.enableSelfPreservation = enableSelfPreservation 250 251 if value, ok := option[optionGenerateUniqueInstId]; ok { 252 h.generateUniqueInstId, _ = value.(bool) 253 } else { 254 h.generateUniqueInstId = false 255 } 256 257 if raw, _ := option[optionCustomValues].(map[interface{}]interface{}); raw != nil { 258 for k, v := range raw { 259 CustomEurekaParameters[k.(string)] = fmt.Sprintf("%v", v) 260 } 261 } 262 263 eurekalog.Infof("[EUREKA] custom eureka parameters: %v", CustomEurekaParameters) 264 return nil 265 } 266 267 func parsePeersToReplicate(defaultNamespace string, replicatePeerObjs []interface{}) map[string][]string { 268 ret := make(map[string][]string) 269 if len(replicatePeerObjs) == 0 { 270 return ret 271 } 272 273 for _, replicatePeerObj := range replicatePeerObjs { 274 replicatePeerStr, ok := replicatePeerObj.(string) 275 if ok { 276 if replicatePeerStr == utils.LocalHost { 277 // If the url represents this host, do not replicate to yourself. 278 continue 279 } 280 peers, exist := ret[defaultNamespace] 281 if !exist { 282 peers = []string{replicatePeerStr} 283 } else { 284 peers = append(peers, replicatePeerStr) 285 } 286 ret[defaultNamespace] = peers 287 288 } else if namespaceReplicatePeerMap, ok := replicatePeerObj.(map[interface{}]interface{}); ok { 289 for k, v := range namespaceReplicatePeerMap { 290 namespace := k.(string) 291 peerObjs := v.([]interface{}) 292 for _, peer := range peerObjs { 293 peerStr, success := peer.(string) 294 295 if success { 296 if peerStr == utils.LocalHost { 297 // If the url represents this host, do not replicate to yourself. 298 continue 299 } 300 peers, exist := ret[namespace] 301 if !exist { 302 peers = []string{peerStr} 303 } else { 304 peers = append(peers, peerStr) 305 } 306 ret[namespace] = peers 307 } 308 } 309 } 310 311 } 312 313 } 314 return ret 315 } 316 317 // Run 启动HTTP API服务器 318 func (h *EurekaServer) Run(errCh chan error) { 319 eurekalog.Infof("start EurekaServer") 320 h.exitCh = make(chan struct{}) 321 h.start = true 322 defer func() { 323 close(h.exitCh) 324 h.start = false 325 }() 326 var err error 327 // 引入功能模块和插件 328 h.namingServer, err = service.GetServer() 329 if err != nil { 330 eurekalog.Errorf("%v", err) 331 errCh <- err 332 return 333 } 334 h.originDiscoverSvr, err = service.GetOriginServer() 335 if err != nil { 336 eurekalog.Errorf("%v", err) 337 errCh <- err 338 return 339 } 340 h.healthCheckServer, err = healthcheck.GetServer() 341 if err != nil { 342 eurekalog.Errorf("%v", err) 343 errCh <- err 344 return 345 } 346 if len(h.replicatePeers) > 0 { 347 h.eventHandlerHandler = &EurekaInstanceEventHandler{ 348 BaseInstanceEventHandler: service.NewBaseInstanceEventHandler(h.namingServer), svr: h} 349 subCtx, err := eventhub.Subscribe(eventhub.InstanceEventTopic, h.eventHandlerHandler) 350 if err != nil { 351 errCh <- err 352 return 353 } 354 h.subCtxs = append(h.subCtxs, subCtx) 355 } 356 h.registerInstanceChain() 357 h.workers = NewApplicationsWorkers(h.refreshInterval, h.deltaExpireInterval, h.enableSelfPreservation, 358 h.namingServer, h.healthCheckServer, h.namespace) 359 h.statis = plugin.GetStatis() 360 // 初始化http server 361 address := fmt.Sprintf("%v:%v", h.listenIP, h.listenPort) 362 363 wsContainer, err := h.createRestfulContainer() 364 if err != nil { 365 errCh <- err 366 return 367 } 368 369 server := http.Server{Addr: address, Handler: wsContainer, WriteTimeout: 2 * time.Minute} 370 371 ln, err := net.Listen("tcp", address) 372 if err != nil { 373 eurekalog.Errorf("net listen(%s) err: %s", address, err.Error()) 374 errCh <- err 375 return 376 } 377 ln = keepalive.NewTcpKeepAliveListener(3*time.Minute, ln.(*net.TCPListener)) 378 // 开启最大连接数限制 379 if h.connLimitConfig != nil && h.connLimitConfig.OpenConnLimit { 380 eurekalog.Infof("http server use max connection limit per ip: %d, http max limit: %d", 381 h.connLimitConfig.MaxConnPerHost, h.connLimitConfig.MaxConnLimit) 382 ln, err = connlimit.NewListener(ln, h.GetProtocol(), h.connLimitConfig) 383 if err != nil { 384 eurekalog.Errorf("conn limit init err: %s", err.Error()) 385 errCh <- err 386 return 387 } 388 } 389 h.server = &server 390 391 // 开始对外服务 392 if h.tlsInfo.IsEmpty() { 393 err = server.Serve(ln) 394 } else { 395 err = server.ServeTLS(ln, h.tlsInfo.CertFile, h.tlsInfo.KeyFile) 396 } 397 if err != nil && err != http.ErrServerClosed { 398 eurekalog.Errorf("%+v", err) 399 if !h.restart { 400 eurekalog.Infof("not in restart progress, broadcast error") 401 errCh <- err 402 } 403 return 404 } 405 eurekalog.Infof("EurekaServer stop") 406 } 407 408 // 创建handler 409 func (h *EurekaServer) createRestfulContainer() (*restful.Container, error) { 410 wsContainer := restful.NewContainer() 411 wsContainer.Filter(h.process) 412 wsContainer.Add(h.GetEurekaV2Server()) 413 wsContainer.Add(h.GetEurekaV1Server()) 414 wsContainer.Add(h.GetEurekaServer()) 415 wsContainer.RecoverHandler(h.recoverFunc) 416 return wsContainer, nil 417 } 418 419 func (h *EurekaServer) recoverFunc(i interface{}, w http.ResponseWriter) { 420 eurekalog.Errorf("panic %+v", i) 421 w.WriteHeader(http.StatusInternalServerError) 422 w.Header().Add(restful.HEADER_ContentType, restful.MIME_JSON) 423 } 424 425 // process 在接收和回复时统一处理请求 426 func (h *EurekaServer) process(req *restful.Request, rsp *restful.Response, chain *restful.FilterChain) { 427 func() { 428 if err := h.preprocess(req, rsp); err != nil { 429 return 430 } 431 432 chain.ProcessFilter(req, rsp) 433 }() 434 435 h.postproccess(req, rsp) 436 } 437 438 func isImportantRequest(req *restful.Request) bool { 439 if req.Request.Method == "POST" || req.Request.Method == "DELETE" { 440 return true 441 } 442 urlStr := req.Request.URL.String() 443 if req.Request.Method == "PUT" && strings.Contains(urlStr, "/status") { 444 return true 445 } 446 return false 447 } 448 449 /** 450 * @brief 请求预处理 451 */ 452 func (h *EurekaServer) preprocess(req *restful.Request, rsp *restful.Response) error { 453 // 设置开始时间 454 req.SetAttribute("start-time", time.Now()) 455 456 if isImportantRequest(req) { 457 // 打印请求 458 accesslog.Info("receive request", 459 zap.String("client-address", req.Request.RemoteAddr), 460 zap.String("user-agent", req.HeaderParameter("User-Agent")), 461 zap.String("method", req.Request.Method), 462 zap.String("url", req.Request.URL.String()), 463 ) 464 } 465 // 限流 466 if err := h.enterRateLimit(req, rsp); err != nil { 467 return err 468 } 469 470 return nil 471 } 472 473 // 访问限制 474 func (h *EurekaServer) enterRateLimit(req *restful.Request, rsp *restful.Response) error { 475 // 检查限流插件是否开启 476 if h.rateLimit == nil { 477 return nil 478 } 479 // IP级限流 480 // 先获取当前请求的address 481 address := req.Request.RemoteAddr 482 segments := strings.Split(address, ":") 483 if len(segments) != 2 { 484 return nil 485 } 486 if ok := h.rateLimit.Allow(plugin.IPRatelimit, segments[0]); !ok { 487 accesslog.Error("ip ratelimit is not allow", zap.String("client", address)) 488 RateLimitResponse(rsp) 489 return errors.New("ip ratelimit is not allow") 490 } 491 492 // 接口级限流 493 apiName := fmt.Sprintf("%s:%s", req.Request.Method, 494 strings.TrimSuffix(req.Request.URL.Path, "/")) 495 if ok := h.rateLimit.Allow(plugin.APIRatelimit, apiName); !ok { 496 accesslog.Error("api ratelimit is not allow", zap.String("client", address), zap.String("api", apiName)) 497 RateLimitResponse(rsp) 498 return errors.New("api ratelimit is not allow") 499 } 500 501 return nil 502 } 503 504 // RateLimitResponse http答复简单封装 505 func RateLimitResponse(rsp *restful.Response) { 506 rsp.WriteHeader(http.StatusTooManyRequests) 507 rsp.Header().Add(restful.HEADER_ContentType, restful.MIME_JSON) 508 } 509 510 /** 511 * @brief 请求后处理:统计 512 */ 513 func (h *EurekaServer) postproccess(req *restful.Request, rsp *restful.Response) { 514 now := time.Now() 515 // 接口调用统计 516 path := req.Request.URL.Path 517 if path != "/" { 518 // 去掉最后一个"/" 519 path = strings.TrimSuffix(path, "/") 520 } 521 startTime := req.Attribute("start-time").(time.Time) 522 523 recordApiCall := true 524 code, ok := req.Attribute(statusCodeHeader).(uint32) 525 if !ok { 526 code = uint32(rsp.StatusCode()) 527 recordApiCall = code != http.StatusNotFound 528 } 529 diff := now.Sub(startTime) 530 // 打印耗时超过1s的请求 531 if diff > time.Second { 532 accesslog.Info("handling time > 1s", 533 zap.String("client-address", req.Request.RemoteAddr), 534 zap.String("user-agent", req.HeaderParameter("User-Agent")), 535 zap.String("method", req.Request.Method), 536 zap.String("url", req.Request.URL.String()), 537 zap.Duration("handling-time", diff), 538 ) 539 } 540 method := getEurekaApi(req.Request.Method, path) 541 542 if recordApiCall { 543 h.statis.ReportCallMetrics(metrics.CallMetric{ 544 API: method, 545 Protocol: "HTTP", 546 Code: int(code), 547 Duration: diff, 548 }) 549 } 550 } 551 552 // getEurekaApi 聚合 eureka 接口,不暴露服务名和实例 id 553 func getEurekaApi(method, path string) string { 554 if path == "" { 555 return "" 556 } 557 if !strings.HasPrefix(path, pathPrefix) { 558 return path 559 } 560 561 pathSlashCount := strings.Count(path, "/") 562 563 switch method { 564 case http.MethodPost: 565 if pathSlashCount == 3 { 566 // POST:/eureka/apps/{application} 567 return operationRegister 568 } 569 case http.MethodGet: 570 if path == "/eureka/apps/delta" { 571 return operationDelta 572 } 573 if pathSlashCount == 3 { 574 // GET:/eureka/apps/{application} 575 return operationAllAppIDInstances 576 } else if pathSlashCount == 4 { 577 // GET:/eureka/apps/{application}/{instanceid} 578 return operationAppIDInstance 579 } 580 case http.MethodDelete: 581 if pathSlashCount == 4 { 582 // DELETE:/eureka/apps/{application}/{instanceid} 583 return operationDeregister 584 } else if strings.HasSuffix(path, statusSuffix) && pathSlashCount == 5 { 585 // DELETE:/eureka/apps/{application}/{instanceid}/status 586 return operationDeleteStatusChange 587 } 588 case http.MethodPut: 589 if pathSlashCount == 4 { 590 // PUT:/eureka/apps/{application}/{instanceid} 591 return operationHeartbeat 592 } else if strings.HasSuffix(path, statusSuffix) && pathSlashCount == 5 { 593 // PUT:/eureka/apps/{application}/{instanceid}/status 594 return operationStatusChange 595 } 596 } 597 598 // GET:/eureka/apps 和其他无法识别的接口直接返回 599 return method + ":" + path 600 } 601 602 // Stop 结束eurekaServer的运行 603 func (h *EurekaServer) Stop() { 604 // 释放connLimit的数据,如果没有开启,也需要执行一下 605 // 目的:防止restart的时候,connLimit冲突 606 connlimit.RemoveLimitListener(h.GetProtocol()) 607 if h.server != nil { 608 _ = h.server.Close() 609 } 610 h.workers.Stop() 611 } 612 613 // Restart 重启eurekaServer 614 func (h *EurekaServer) Restart( 615 option map[string]interface{}, api map[string]apiserver.APIConfig, errCh chan error) error { 616 eurekalog.Infof("restart httpserver new config: %+v", option) 617 // 备份一下option 618 backupOption := h.option 619 // 备份一下api 620 backupAPI := h.openAPI 621 622 // 设置restart标记,防止stop的时候把错误抛出 623 h.restart = true 624 // 关闭httpserver 625 h.Stop() 626 // 等待httpserver退出 627 if h.start { 628 <-h.exitCh 629 } 630 631 eurekalog.Infof("old httpserver has stopped, begin restart httpserver") 632 633 if err := h.Initialize(context.Background(), option, api); err != nil { 634 h.restart = false 635 if initErr := h.Initialize(context.Background(), backupOption, backupAPI); initErr != nil { 636 eurekalog.Errorf("start httpserver with backup cfg err: %s", initErr.Error()) 637 return initErr 638 } 639 go h.Run(errCh) 640 641 eurekalog.Errorf("restart httpserver initialize err: %s", err.Error()) 642 return err 643 } 644 645 eurekalog.Infof("init httpserver successfully, restart it") 646 h.restart = false 647 go h.Run(errCh) 648 return nil 649 }