github.com/polarismesh/polaris@v1.17.8/apiserver/httpserver/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 httpserver 19 20 import ( 21 "context" 22 "fmt" 23 "net" 24 "net/http" 25 "net/http/pprof" 26 "strings" 27 "time" 28 29 "github.com/emicklei/go-restful/v3" 30 "github.com/gogo/protobuf/jsonpb" 31 "github.com/pkg/errors" 32 "github.com/polarismesh/specification/source/go/api/v1/service_manage" 33 "go.uber.org/zap" 34 35 "github.com/polarismesh/polaris/admin" 36 "github.com/polarismesh/polaris/apiserver" 37 confighttp "github.com/polarismesh/polaris/apiserver/httpserver/config" 38 v1 "github.com/polarismesh/polaris/apiserver/httpserver/discover/v1" 39 v2 "github.com/polarismesh/polaris/apiserver/httpserver/discover/v2" 40 httpcommon "github.com/polarismesh/polaris/apiserver/httpserver/utils" 41 "github.com/polarismesh/polaris/auth" 42 api "github.com/polarismesh/polaris/common/api/v1" 43 "github.com/polarismesh/polaris/common/conn/keepalive" 44 connlimit "github.com/polarismesh/polaris/common/conn/limit" 45 commonlog "github.com/polarismesh/polaris/common/log" 46 "github.com/polarismesh/polaris/common/metrics" 47 "github.com/polarismesh/polaris/common/secure" 48 "github.com/polarismesh/polaris/common/utils" 49 "github.com/polarismesh/polaris/config" 50 "github.com/polarismesh/polaris/namespace" 51 "github.com/polarismesh/polaris/plugin" 52 "github.com/polarismesh/polaris/service" 53 "github.com/polarismesh/polaris/service/healthcheck" 54 ) 55 56 // HTTPServer HTTP API服务器 57 type HTTPServer struct { 58 listenIP string 59 listenPort uint32 60 connLimitConfig *connlimit.Config 61 tlsInfo *secure.TLSInfo 62 option map[string]interface{} 63 openAPI map[string]apiserver.APIConfig 64 start bool 65 restart bool 66 exitCh chan struct{} 67 68 enablePprof bool 69 enableSwagger bool 70 71 server *http.Server 72 maintainServer admin.AdminOperateServer 73 namespaceServer namespace.NamespaceOperateServer 74 namingServer service.DiscoverServer 75 configServer config.ConfigCenterServer 76 healthCheckServer *healthcheck.Server 77 rateLimit plugin.Ratelimit 78 statis plugin.Statis 79 whitelist plugin.Whitelist 80 81 discoverV1 v1.HTTPServerV1 82 discoverV2 v2.HTTPServerV2 83 configSvr confighttp.HTTPServer 84 85 userMgn auth.UserServer 86 strategyMgn auth.StrategyServer 87 } 88 89 const ( 90 // Discover discover string 91 Discover string = "Discover" 92 defaultAlivePeriodTime = 3 * time.Minute 93 ) 94 95 // GetPort 获取端口 96 func (h *HTTPServer) GetPort() uint32 { 97 return h.listenPort 98 } 99 100 // GetProtocol 获取Server的协议 101 func (h *HTTPServer) GetProtocol() string { 102 return "http" 103 } 104 105 // Initialize 初始化HTTP API服务器 106 func (h *HTTPServer) Initialize(_ context.Context, option map[string]interface{}, 107 apiConf map[string]apiserver.APIConfig) error { 108 h.option = option 109 h.openAPI = apiConf 110 h.listenIP = option["listenIP"].(string) 111 h.listenPort = uint32(option["listenPort"].(int)) 112 h.enablePprof, _ = option["enablePprof"].(bool) 113 h.enableSwagger, _ = option["enableSwagger"].(bool) 114 // 连接数限制的配置 115 if raw, _ := option["connLimit"].(map[interface{}]interface{}); raw != nil { 116 connLimitConfig, err := connlimit.ParseConnLimitConfig(raw) 117 if err != nil { 118 return err 119 } 120 h.connLimitConfig = connLimitConfig 121 } 122 if rateLimit := plugin.GetRatelimit(); rateLimit != nil { 123 log.Infof("http server open the ratelimit") 124 h.rateLimit = rateLimit 125 } 126 127 if whitelist := plugin.GetWhitelist(); whitelist != nil { 128 log.Infof("http server open the whitelist") 129 h.whitelist = whitelist 130 } 131 132 // tls 配置信息 133 if raw, _ := option["tls"].(map[interface{}]interface{}); raw != nil { 134 tlsConfig, err := secure.ParseTLSConfig(raw) 135 if err != nil { 136 return err 137 } 138 h.tlsInfo = &secure.TLSInfo{ 139 CertFile: tlsConfig.CertFile, 140 KeyFile: tlsConfig.KeyFile, 141 TrustedCAFile: tlsConfig.TrustedCAFile, 142 } 143 } 144 145 metrics.SetMetricsPort(int32(h.listenPort)) 146 return nil 147 } 148 149 // Run 启动HTTP API服务器 150 func (h *HTTPServer) Run(errCh chan error) { 151 log.Infof("start httpserver") 152 h.exitCh = make(chan struct{}, 1) 153 h.start = true 154 defer func() { 155 close(h.exitCh) 156 h.start = false 157 }() 158 159 var err error 160 161 h.maintainServer, err = admin.GetServer() 162 if err != nil { 163 log.Errorf("%v", err) 164 errCh <- err 165 return 166 } 167 168 // 引入命名空间模块 169 h.namespaceServer, err = namespace.GetServer() 170 if err != nil { 171 log.Errorf("%v", err) 172 errCh <- err 173 return 174 } 175 176 // 引入功能模块和插件 177 h.namingServer, err = service.GetServer() 178 if err != nil { 179 log.Errorf("%v", err) 180 errCh <- err 181 return 182 } 183 184 userMgn, err := auth.GetUserServer() 185 if err != nil { 186 log.Errorf("%v", err) 187 errCh <- err 188 return 189 } 190 191 strategyMgn, err := auth.GetStrategyServer() 192 if err != nil { 193 log.Errorf("%v", err) 194 errCh <- err 195 return 196 } 197 198 h.userMgn = userMgn 199 h.strategyMgn = strategyMgn 200 201 h.healthCheckServer, err = healthcheck.GetServer() 202 if err != nil { 203 log.Errorf("%v", err) 204 errCh <- err 205 return 206 } 207 h.statis = plugin.GetStatis() 208 209 // 初始化配置中心模块 210 h.configServer, err = config.GetServer() 211 if err != nil { 212 log.Errorf("set config server to http server error. %v", err) 213 errCh <- err 214 return 215 } 216 217 h.discoverV1 = *v1.NewV1Server(h.namespaceServer, h.namingServer, h.healthCheckServer) 218 h.discoverV2 = *v2.NewV2Server(h.namespaceServer, h.namingServer, h.healthCheckServer) 219 h.configSvr = *confighttp.NewServer(h.maintainServer, h.namespaceServer, h.configServer) 220 221 // 初始化http server 222 address := fmt.Sprintf("%v:%v", h.listenIP, h.listenPort) 223 224 var wsContainer *restful.Container 225 wsContainer, err = h.createRestfulContainer() 226 if err != nil { 227 errCh <- err 228 return 229 } 230 231 server := http.Server{Addr: address, Handler: wsContainer, WriteTimeout: 1 * time.Minute} 232 var ln net.Listener 233 ln, err = net.Listen("tcp", address) 234 if err != nil { 235 log.Errorf("net listen(%s) err: %s", address, err.Error()) 236 errCh <- err 237 return 238 } 239 240 ln = keepalive.NewTcpKeepAliveListener(defaultAlivePeriodTime, ln.(*net.TCPListener)) 241 // 开启最大连接数限制 242 if h.connLimitConfig != nil && h.connLimitConfig.OpenConnLimit { 243 log.Infof("http server use max connection limit per ip: %d, http max limit: %d", 244 h.connLimitConfig.MaxConnPerHost, h.connLimitConfig.MaxConnLimit) 245 ln, err = connlimit.NewListener(ln, h.GetProtocol(), h.connLimitConfig) 246 if err != nil { 247 log.Errorf("conn limit init err: %s", err.Error()) 248 errCh <- err 249 return 250 } 251 } 252 h.server = &server 253 254 // 开始对外服务 255 if h.tlsInfo.IsEmpty() { 256 err = server.Serve(ln) 257 } else { 258 err = server.ServeTLS(ln, h.tlsInfo.CertFile, h.tlsInfo.KeyFile) 259 } 260 if err != nil { 261 log.Errorf("%+v", err) 262 if !h.restart { 263 log.Infof("not in restart progress, broadcast error") 264 errCh <- err 265 } 266 267 return 268 } 269 270 log.Infof("httpserver stop") 271 } 272 273 // Stop shutdown server 274 func (h *HTTPServer) Stop() { 275 // 释放connLimit的数据,如果没有开启,也需要执行一下 276 // 目的:防止restart的时候,connLimit冲突 277 connlimit.RemoveLimitListener(h.GetProtocol()) 278 if h.server != nil { 279 _ = h.server.Close() 280 } 281 } 282 283 // Restart restart server 284 func (h *HTTPServer) Restart(option map[string]interface{}, apiConf map[string]apiserver.APIConfig, 285 errCh chan error) error { 286 log.Infof("restart httpserver new config: %+v", option) 287 // 备份一下option 288 backupOption := h.option 289 // 备份一下api 290 backupAPI := h.openAPI 291 292 // 设置restart标记,防止stop的时候把错误抛出 293 h.restart = true 294 // 关闭httpserver 295 h.Stop() 296 // 等待httpserver退出 297 if h.start { 298 <-h.exitCh 299 } 300 301 log.Infof("old httpserver has stopped, begin restart httpserver") 302 303 ctx := context.Background() 304 if err := h.Initialize(ctx, option, apiConf); err != nil { 305 h.restart = false 306 if initErr := h.Initialize(ctx, backupOption, backupAPI); initErr != nil { 307 log.Errorf("start httpserver with backup cfg err: %s", initErr.Error()) 308 return initErr 309 } 310 go h.Run(errCh) 311 312 log.Errorf("restart httpserver initialize err: %s", err.Error()) 313 return err 314 } 315 316 log.Infof("init httpserver successfully, restart it") 317 h.restart = false 318 go h.Run(errCh) 319 return nil 320 } 321 322 // createRestfulContainer create handler 323 func (h *HTTPServer) createRestfulContainer() (*restful.Container, error) { 324 wsContainer := restful.NewContainer() 325 wsContainer.RecoverHandler(h.recoverFunc) 326 327 // 增加CORS TODO 328 cors := restful.CrossOriginResourceSharing{ 329 // ExposeHeaders: []string{"X-My-Header"}, 330 AllowedHeaders: []string{"Content-Type", "Accept", "Request-Id"}, 331 AllowedMethods: []string{"GET", "POST", "PUT"}, 332 CookiesAllowed: false, 333 Container: wsContainer} 334 wsContainer.Filter(cors.Filter) 335 336 // Incr container filter to respond to OPTIONS 337 wsContainer.Filter(wsContainer.OPTIONSFilter) 338 339 wsContainer.Filter(h.process) 340 341 for name, apiConfig := range h.openAPI { 342 switch name { 343 case "admin": 344 if apiConfig.Enable { 345 wsContainer.Add(h.GetIndexServer()) 346 wsContainer.Add(h.GetAdminAccessServer()) 347 } 348 case "console": 349 if apiConfig.Enable { 350 namingServiceV1, err := h.discoverV1.GetNamingConsoleAccessServer(apiConfig.Include) 351 if err != nil { 352 return nil, err 353 } 354 wsContainer.Add(namingServiceV1) 355 356 namingServiceV2, err := h.discoverV2.GetNamingConsoleAccessServer(apiConfig.Include) 357 if err != nil { 358 return nil, err 359 } 360 wsContainer.Add(namingServiceV2) 361 362 configService, err := h.configSvr.GetConsoleAccessServer(apiConfig.Include) 363 if err != nil { 364 return nil, err 365 } 366 wsContainer.Add(configService) 367 368 ws := new(restful.WebService) 369 ws.Path("/core/v1").Consumes(restful.MIME_JSON).Produces(restful.MIME_JSON) 370 if err := h.GetClientServer(ws); err != nil { 371 return nil, err 372 } 373 if err := h.GetCoreV1ConsoleAccessServer(ws, apiConfig.Include); err != nil { 374 return nil, err 375 } 376 if err := h.GetAuthServer(ws); err != nil { 377 return nil, err 378 } 379 380 prometheusSvc, err := h.GetPrometheusDiscoveryServer(apiConfig.Include) 381 if err != nil { 382 return nil, err 383 } 384 wsContainer.Add(prometheusSvc) 385 386 wsContainer.Add(ws) 387 } 388 case "client": 389 if apiConfig.Enable { 390 ws := new(restful.WebService) 391 ws.Path("/v1").Consumes(restful.MIME_JSON).Produces(restful.MIME_JSON) 392 393 if err := h.discoverV1.GetClientAccessServer(ws, apiConfig.Include); err != nil { 394 return nil, err 395 } 396 if err := h.configSvr.GetClientAccessServer(ws, apiConfig.Include); err != nil { 397 return nil, err 398 } 399 wsContainer.Add(ws) 400 } 401 default: 402 log.Warnf("api %s does not exist in httpserver", name) 403 } 404 } 405 406 if h.enablePprof { 407 h.enablePprofAccess(wsContainer) 408 } 409 410 if h.enableSwagger { 411 h.enableSwaggerAPI(wsContainer) 412 } 413 // 收集插件的 endpoint 数据 414 h.enablePluginDebugAccess(wsContainer) 415 h.enablePrometheusAccess(wsContainer) 416 return wsContainer, nil 417 } 418 419 // enablePprofAccess 开启pprof接口 420 func (h *HTTPServer) enablePprofAccess(wsContainer *restful.Container) { 421 log.Infof("open http access for pprof") 422 wsContainer.Handle("/debug/pprof/", http.HandlerFunc(pprof.Index)) 423 wsContainer.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline)) 424 wsContainer.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile)) 425 wsContainer.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol)) 426 } 427 428 // enablePrometheusAccess 开启 Prometheus 接口 429 func (h *HTTPServer) enablePrometheusAccess(wsContainer *restful.Container) { 430 log.Infof("open http access for prometheus") 431 432 wsContainer.Handle("/metrics", metrics.GetHttpHandler()) 433 } 434 435 // enablePluginDebugAccess . 436 func (h *HTTPServer) enablePluginDebugAccess(wsContainer *restful.Container) { 437 log.Infof("open http access for plugin") 438 for _, checker := range h.healthCheckServer.Checkers() { 439 handlers := checker.DebugHandlers() 440 for i := range handlers { 441 wsContainer.Handle(handlers[i].Path, handlers[i].Handler) 442 } 443 } 444 } 445 446 // process 在接收和回复时统一处理请求 447 func (h *HTTPServer) process(req *restful.Request, rsp *restful.Response, chain *restful.FilterChain) { 448 func() { 449 if err := h.preprocess(req, rsp); err != nil { 450 return 451 } 452 453 chain.ProcessFilter(req, rsp) 454 }() 455 456 h.postProcess(req, rsp) 457 } 458 459 // preprocess 请求预处理 460 func (h *HTTPServer) preprocess(req *restful.Request, rsp *restful.Response) error { 461 // 设置开始时间 462 req.SetAttribute("start-time", time.Now()) 463 464 // 处理请求ID 465 requestID := req.HeaderParameter("Request-Id") 466 if requestID == "" { 467 // TODO: 设置请求ID 468 } 469 470 platformID := req.HeaderParameter("Platform-Id") 471 requestURL := req.Request.URL.String() 472 if !strings.Contains(requestURL, Discover) { 473 // 打印请求 474 log.Info("receive request", 475 zap.String("client-address", req.Request.RemoteAddr), 476 zap.String("user-agent", req.HeaderParameter("User-Agent")), 477 utils.ZapRequestID(requestID), 478 zap.String("platform-id", platformID), 479 zap.String("method", req.Request.Method), 480 zap.String("url", requestURL), 481 ) 482 } 483 484 // 管理端接口访问鉴权 485 if strings.Contains(requestURL, "naming") { 486 if err := h.enterAuth(req, rsp); err != nil { 487 return err 488 } 489 } 490 491 // 限流 492 if err := h.enterRateLimit(req, rsp); err != nil { 493 return err 494 } 495 496 return nil 497 } 498 499 // postProcess 请求后处理:统计 500 func (h *HTTPServer) postProcess(req *restful.Request, rsp *restful.Response) { 501 now := time.Now() 502 503 // 接口调用统计 504 path := req.Request.URL.Path 505 if path != "/" { 506 // 去掉最后一个"/" 507 path = strings.TrimSuffix(path, "/") 508 } 509 method := req.Request.Method + ":" + path 510 startTime := req.Attribute("start-time").(time.Time) 511 code, ok := req.Attribute(utils.PolarisCode).(uint32) 512 513 recordApiCall := true 514 if !ok { 515 code = uint32(rsp.StatusCode()) 516 recordApiCall = code != http.StatusNotFound 517 } 518 519 diff := now.Sub(startTime) 520 // 打印耗时超过1s的请求 521 if diff > time.Second { 522 var scope *commonlog.Scope 523 if strings.Contains(path, "naming") { 524 scope = namingLog 525 } else { 526 scope = configLog 527 } 528 529 scope.Info("handling time > 1s", 530 zap.String("client-address", req.Request.RemoteAddr), 531 zap.String("user-agent", req.HeaderParameter("User-Agent")), 532 utils.ZapRequestID(req.HeaderParameter("Request-Id")), 533 zap.String("method", req.Request.Method), 534 zap.String("url", req.Request.URL.String()), 535 zap.Duration("handling-time", diff), 536 ) 537 } 538 539 if recordApiCall { 540 h.statis.ReportCallMetrics(metrics.CallMetric{ 541 Type: metrics.ServerCallMetric, 542 API: method, 543 Protocol: "HTTP", 544 Code: int(code), 545 Duration: diff, 546 }) 547 } 548 } 549 550 // enterAuth 访问鉴权 551 func (h *HTTPServer) enterAuth(req *restful.Request, rsp *restful.Response) error { 552 // 判断白名单插件是否开启 553 if h.whitelist == nil { 554 return nil 555 } 556 557 rid := req.HeaderParameter("Request-Id") 558 559 address := req.Request.RemoteAddr 560 segments := strings.Split(address, ":") 561 if len(segments) != 2 { 562 return nil 563 } 564 if !h.whitelist.Contain(segments[0]) { 565 log.Error("http access is not allowed", 566 zap.String("client", address), 567 utils.ZapRequestID(rid)) 568 httpcommon.HTTPResponse(req, rsp, api.NotAllowedAccess) 569 return errors.New("http access is not allowed") 570 } 571 return nil 572 } 573 574 // enterRateLimit 访问限制 575 func (h *HTTPServer) enterRateLimit(req *restful.Request, rsp *restful.Response) error { 576 // 检查限流插件是否开启 577 if h.rateLimit == nil { 578 return nil 579 } 580 581 rid := req.HeaderParameter("Request-Id") 582 // IP级限流 583 // 先获取当前请求的address 584 address := req.Request.RemoteAddr 585 segments := strings.Split(address, ":") 586 if len(segments) != 2 { 587 return nil 588 } 589 if ok := h.rateLimit.Allow(plugin.IPRatelimit, segments[0]); !ok { 590 log.Error("ip ratelimit is not allow", zap.String("client", address), 591 utils.ZapRequestID(rid)) 592 httpcommon.HTTPResponse(req, rsp, api.IPRateLimit) 593 return errors.New("ip ratelimit is not allow") 594 } 595 596 // 接口级限流 597 apiName := fmt.Sprintf("%s:%s", req.Request.Method, 598 strings.TrimSuffix(req.Request.URL.Path, "/")) 599 if ok := h.rateLimit.Allow(plugin.APIRatelimit, apiName); !ok { 600 log.Error("api ratelimit is not allow", zap.String("client", address), 601 utils.ZapRequestID(rid), zap.String("api", apiName)) 602 httpcommon.HTTPResponse(req, rsp, api.APIRateLimit) 603 return errors.New("api ratelimit is not allow") 604 } 605 606 return nil 607 } 608 609 func (h *HTTPServer) recoverFunc(i interface{}, w http.ResponseWriter) { 610 log.Errorf("panic %+v", i) 611 obj := &service_manage.Response{} 612 613 status := api.CalcCode(obj) 614 if code := obj.GetCode().GetValue(); code != api.ExecuteSuccess { 615 w.Header().Add(utils.PolarisCode, fmt.Sprintf("%d", code)) 616 w.Header().Add(utils.PolarisMessage, api.Code2Info(code)) 617 } 618 w.WriteHeader(status) 619 620 m := jsonpb.Marshaler{Indent: " ", EmitDefaults: true} 621 _ = m.Marshal(w, obj) 622 }