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  }