123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234 |
- package servers
- import (
- "context"
- "errors"
- "fmt"
- "github.com/gin-contrib/pprof"
- "github.com/gin-gonic/gin"
- ginprometheus "github.com/zsais/go-gin-prometheus"
- "gogs.tyduyong.com/duyong/dy-admin/internal/iam/config"
- "gogs.tyduyong.com/duyong/dy-pkg/app/middleware"
- "gogs.tyduyong.com/duyong/dy-pkg/app/response"
- "gogs.tyduyong.com/duyong/dy-pkg/app/version"
- "gogs.tyduyong.com/duyong/dy-pkg/logs"
- "log"
- "net"
- "net/http"
- "strconv"
- "strings"
- "time"
- )
- // CertKey 证书结构
- type CertKey struct {
- // CertFile is a file containing a PEM-encoded certificate, and possibly the complete certificate chain
- CertFile string
- // KeyFile is a file containing a PEM-encoded private key for the certificate specified by CertFile
- KeyFile string
- }
- // SecureServeInfo 开启https时的结构
- type SecureServeInfo struct {
- BindAddress string
- BindPort int
- CertKey CertKey
- }
- func (s *SecureServeInfo) Address() string {
- return net.JoinHostPort(s.BindAddress, strconv.Itoa(s.BindPort))
- }
- // httpAPIServer Http&Https封装
- type httpAPIServer struct {
- *gin.Engine
- middlewares []string
- healthz bool
- enableMetrics bool
- enableProfiling bool
- insecureAddress string
- secureInfo *SecureServeInfo
- insecureServer, secureServer *http.Server
- }
- // newHttpServer 构建http服务配置
- func newHttpServer(cfg *config.Config) *httpAPIServer {
- gin.SetMode(cfg.ServerRunOptions.Mode)
- httpServer := &httpAPIServer{
- Engine: gin.New(),
- middlewares: cfg.ServerRunOptions.Middlewares,
- healthz: cfg.ServerRunOptions.Healthz,
- enableMetrics: cfg.FeatureOptions.EnableMetrics,
- enableProfiling: cfg.FeatureOptions.EnableProfiling,
- insecureAddress: net.JoinHostPort(cfg.InsecureServeOptions.BindAddress, strconv.Itoa(cfg.InsecureServeOptions.BindPort)),
- secureInfo: &SecureServeInfo{
- BindAddress: cfg.SecureServingOptions.BindAddress,
- BindPort: cfg.SecureServingOptions.BindPort,
- CertKey: CertKey{
- CertFile: cfg.SecureServingOptions.ServerCert.CertKey.CertFile,
- KeyFile: cfg.SecureServingOptions.ServerCert.CertKey.KeyFile,
- },
- },
- }
- httpServer.Setup()
- httpServer.InstallMiddlewares()
- httpServer.InstallAPIs()
- return httpServer
- }
- func (s *httpAPIServer) Setup() {
- gin.DebugPrintRouteFunc = func(httpMethod, absolutePath, handlerName string, nuHandlers int) {
- logs.Infof("%-6s %-s --> %s (%d handlers)", httpMethod, absolutePath, handlerName, nuHandlers)
- }
- }
- // InstallMiddlewares 根据配置,选定路由中间件
- func (s *httpAPIServer) InstallMiddlewares() {
- // 必选中间件
- s.Use(middleware.RequestID())
- s.Use(middleware.Context())
- // install custom middlewares
- for _, m := range s.middlewares {
- mw, ok := middleware.Middlewares[m]
- if !ok {
- logs.Warnf("can not find middleware: %s", m)
- continue
- }
- logs.Infof("install middleware: %s", m)
- s.Use(mw)
- }
- }
- // InstallAPIs 根据配置,设置制定的接口
- func (s *httpAPIServer) InstallAPIs() {
- // 健康检查接口
- if s.healthz {
- s.GET("/healthz", func(c *gin.Context) {
- response.WriteResponse(c, nil, map[string]string{"status": "ok"})
- })
- }
- // 监控接口
- if s.enableMetrics {
- prometheus := ginprometheus.NewPrometheus("gin")
- prometheus.Use(s.Engine)
- }
- // install pprof handler
- if s.enableProfiling {
- pprof.Register(s.Engine)
- }
- s.GET("/version", func(c *gin.Context) {
- response.WriteResponse(c, nil, version.Get())
- })
- }
- // Run 启动服务
- func (s *httpAPIServer) Run() {
- s.insecureServer = &http.Server{
- Addr: s.insecureAddress,
- Handler: s,
- }
- s.secureServer = &http.Server{
- Addr: s.secureInfo.Address(),
- Handler: s,
- }
- go func() {
- logs.Infof("Start to listening the incoming requests on http address: %s", s.insecureAddress)
- if err := s.insecureServer.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
- logs.Fatal(err.Error())
- return
- }
- logs.Infof("Server on %s stopped", s.insecureAddress)
- }()
- go func() {
- key, cert := s.secureInfo.CertKey.KeyFile, s.secureInfo.CertKey.CertFile
- if cert == "" || key == "" || s.secureInfo.BindPort == 0 {
- return
- }
- logs.Infof("Start to listening the incoming requests on https address: %s", s.secureInfo.Address())
- if err := s.secureServer.ListenAndServeTLS(cert, key); err != nil && !errors.Is(err, http.ErrServerClosed) {
- logs.Fatal(err.Error())
- return
- }
- logs.Infof("Server on %s stopped", s.secureInfo.Address())
- }()
- // Ping the server to make sure the router is working.
- go func() {
- if s.healthz {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
- if s.healthz {
- if err := s.ping(ctx); err != nil {
- return
- }
- }
- }
- }()
- return
- }
- // Close 关闭服务
- func (s *httpAPIServer) Close() {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
- if err := s.secureServer.Shutdown(ctx); err != nil {
- logs.Warnf("Shutdown secure server failed: %s", err.Error())
- }
- if err := s.insecureServer.Shutdown(ctx); err != nil {
- logs.Warnf("Shutdown insecure server failed: %s", err.Error())
- }
- }
- func (s *httpAPIServer) ping(ctx context.Context) error {
- url := fmt.Sprintf("http://%s/healthz", s.insecureAddress)
- if strings.Contains(s.insecureAddress, "0.0.0.0") {
- url = fmt.Sprintf("http://127.0.0.1:%s/healthz", strings.Split(s.insecureAddress, ":")[1])
- }
- for {
- req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
- if err != nil {
- return err
- }
- resp, err := http.DefaultClient.Do(req)
- if err == nil && resp.StatusCode == http.StatusOK {
- logs.Info("The router has been deployed successfully.")
- resp.Body.Close()
- return nil
- }
- // Sleep for a second to continue the next ping.
- logs.Info("Waiting for the router, retry in 1 second.")
- time.Sleep(1 * time.Second)
- select {
- case <-ctx.Done():
- log.Fatal("can not ping http server with in the specified time interval.")
- default:
- }
- }
- }
|