github.com/isyscore/isc-gobase@v1.5.3-0.20231218061332-cbc7451899e9/server/server.go (about)

     1  package server
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"github.com/isyscore/isc-gobase/server/rsp"
     7  	"github.com/isyscore/isc-gobase/store"
     8  	"io"
     9  	"net/http"
    10  	"os"
    11  	"os/signal"
    12  	"strings"
    13  	"sync"
    14  	"syscall"
    15  	"time"
    16  
    17  	"github.com/gin-contrib/pprof"
    18  	"github.com/isyscore/isc-gobase/bean"
    19  	"github.com/isyscore/isc-gobase/debug"
    20  	"github.com/isyscore/isc-gobase/listener"
    21  	swaggerFiles "github.com/swaggo/files"
    22  	ginSwagger "github.com/swaggo/gin-swagger"
    23  
    24  	"github.com/isyscore/isc-gobase/config"
    25  	"github.com/isyscore/isc-gobase/isc"
    26  
    27  	"github.com/isyscore/isc-gobase/logger"
    28  
    29  	"github.com/gin-gonic/gin"
    30  	"github.com/isyscore/isc-gobase/websocket"
    31  )
    32  
    33  type HttpMethod int
    34  
    35  const (
    36  	HmAll HttpMethod = iota
    37  	HmGet
    38  	HmPost
    39  	HmPut
    40  	HmDelete
    41  	HmOptions
    42  	HmHead
    43  	HmGetPost
    44  )
    45  
    46  var GoBaseVersion = "1.5.1"
    47  var ApiPrefix = "/api"
    48  
    49  var engine *gin.Engine = nil
    50  var pprofHave = false
    51  
    52  var loadLock sync.Mutex
    53  var serverLoaded = false
    54  
    55  //type methodTrees []methodTree
    56  
    57  var ginHandlers []gin.HandlerFunc
    58  
    59  func init() {
    60  	isc.PrintBanner()
    61  	config.LoadConfig()
    62  	printVersionAndProfile()
    63  }
    64  
    65  // 提供给外部注册使用
    66  func AddGinHandlers(handler gin.HandlerFunc) {
    67  	if nil == ginHandlers {
    68  		var ginHandlersTem []gin.HandlerFunc
    69  		ginHandlers = ginHandlersTem
    70  	}
    71  
    72  	ginHandlers = append(ginHandlers, handler)
    73  }
    74  
    75  func InitServer() {
    76  	loadLock.Lock()
    77  	defer loadLock.Unlock()
    78  	if serverLoaded {
    79  		return
    80  	}
    81  	if !config.ExistConfigFile() || !config.GetValueBoolDefault("base.server.enable", false) {
    82  		return
    83  	}
    84  
    85  	if !config.ExistConfigFile() {
    86  		logger.Error("没有找到任何配置文件,服务启动失败")
    87  		return
    88  	}
    89  	mode := config.GetValueStringDefault("base.server.gin.mode", "release")
    90  	if "debug" == mode {
    91  		gin.SetMode(gin.DebugMode)
    92  	} else if "test" == mode {
    93  		gin.SetMode(gin.TestMode)
    94  	} else if "release" == mode {
    95  		gin.SetMode(gin.ReleaseMode)
    96  		gin.DefaultWriter = io.Discard
    97  	} else {
    98  		gin.SetMode(gin.ReleaseMode)
    99  		gin.DefaultWriter = io.Discard
   100  	}
   101  
   102  	engine = gin.New()
   103  
   104  	if config.GetValueBoolDefault("base.debug.enable", true) {
   105  		// 注册pprof
   106  		if config.GetValueBoolDefault("base.server.gin.pprof.enable", false) {
   107  			pprofHave = true
   108  			pprof.Register(engine)
   109  		}
   110  	}
   111  
   112  	if config.GetValueBoolDefault("base.server.cors.enable", true) {
   113  		engine.Use(Cors())
   114  	}
   115  	engine.Use(gin.Recovery(), ErrHandler())
   116  	engine.Use(RequestSaveHandler())
   117  	engine.Use(rsp.ResponseHandler())
   118  	for _, handler := range ginHandlers {
   119  		engine.Use(handler)
   120  	}
   121  
   122  	// 注册 健康检查endpoint
   123  	if config.GetValueBoolDefault("base.endpoint.health.enable", false) {
   124  		RegisterHealthCheckEndpoint(apiPreAndModule())
   125  	}
   126  
   127  	if config.GetValueBoolDefault("base.debug.enable", true) {
   128  		// 注册 配置查看和变更功能
   129  		if config.GetValueBoolDefault("base.endpoint.config.enable", false) {
   130  			RegisterConfigWatchEndpoint(apiPreAndModule())
   131  		}
   132  
   133  		// 注册 bean管理的功能
   134  		if config.GetValueBoolDefault("base.endpoint.bean.enable", false) {
   135  			RegisterBeanWatchEndpoint(apiPreAndModule())
   136  		}
   137  
   138  		// 注册 debug的帮助命令
   139  		RegisterHelpEndpoint(apiPreAndModule())
   140  	}
   141  
   142  	// 注册 swagger的功能
   143  	if config.GetValueBoolDefault("base.swagger.enable", false) {
   144  		RegisterSwaggerEndpoint()
   145  	}
   146  
   147  	// 添加配置变更事件的监听
   148  	listener.AddListener(listener.EventOfConfigChange, ConfigChangeListener)
   149  
   150  	logger.InitLog()
   151  	serverLoaded = true
   152  }
   153  
   154  func ConfigChangeListener(event listener.BaseEvent) {
   155  	ev := event.(listener.ConfigChangeEvent)
   156  	if ev.Key == "base.server.gin.pprof.enable" {
   157  		if isc.ToBool(ev.Value) && !pprofHave {
   158  			pprofHave = true
   159  			pprof.Register(engine)
   160  		}
   161  	}
   162  }
   163  
   164  func ErrHandler() gin.HandlerFunc {
   165  	return func(c *gin.Context) {
   166  		defer func() {
   167  			if err := recover(); err != nil {
   168  				rsp.FailedOfStandard(c, 500, fmt.Sprintf("业务异常:%v", err))
   169  				return
   170  			}
   171  		}()
   172  		c.Next()
   173  	}
   174  }
   175  
   176  func apiPreAndModule() string {
   177  	ap := config.GetValueStringDefault("base.api.prefix", "")
   178  	if ap != "" {
   179  		ApiPrefix = ap
   180  	}
   181  	return ApiPrefix + "/" + config.ApiModule
   182  }
   183  
   184  func printVersionAndProfile() {
   185  	fmt.Printf("----------------------------- isc-gobase: %s --------------------------\n", GoBaseVersion)
   186  	fmt.Printf("profile:%s\n", config.CurrentProfile)
   187  	fmt.Printf("--------------------------------------------------------------------------\n")
   188  }
   189  
   190  func Run() {
   191  	StartServer()
   192  }
   193  
   194  func StartServer() {
   195  	if !checkEngine() {
   196  		return
   197  	}
   198  
   199  	if engine == nil {
   200  		return
   201  	}
   202  
   203  	listener.PublishEvent(listener.ServerRunStartEvent{})
   204  
   205  	if !config.GetValueBoolDefault("base.server.enable", true) {
   206  		return
   207  	}
   208  
   209  	logger.Info("开始启动服务")
   210  	port := config.GetValueIntDefault("base.server.port", 8080)
   211  	logger.Info("服务端口号: %d", port)
   212  
   213  	graceRun(port)
   214  }
   215  
   216  func graceRun(port int) {
   217  	engineServer := &http.Server{Addr: fmt.Sprintf(":%d", port), Handler: engine}
   218  	go func() {
   219  		if err := engineServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
   220  			logger.Error("启动服务异常 (%v)", err)
   221  		} else {
   222  			// 发送服务关闭事件
   223  			listener.PublishEvent(listener.ServerStopEvent{})
   224  		}
   225  	}()
   226  
   227  	// 发送服务启动事件
   228  	listener.PublishEvent(listener.ServerRunFinishEvent{})
   229  	quit := make(chan os.Signal, 1)
   230  	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
   231  	<-quit
   232  	logger.Warn("服务端准备关闭...")
   233  	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
   234  	defer cancel()
   235  	if err := engineServer.Shutdown(ctx); err != nil {
   236  		logger.Warn("服务关闭异常: %v", err.Error())
   237  	}
   238  	logger.Warn("服务端退出")
   239  }
   240  
   241  func RegisterStatic(relativePath string, rootPath string) gin.IRoutes {
   242  	if !checkEngine() {
   243  		return nil
   244  	}
   245  	engine.Static(relativePath, rootPath)
   246  	return engine
   247  }
   248  
   249  func RegisterStaticFile(relativePath string, filePath string) gin.IRoutes {
   250  	if !checkEngine() {
   251  		return nil
   252  	}
   253  	engine.StaticFile(relativePath, filePath)
   254  	return engine
   255  }
   256  
   257  func RegisterPlugin(plugin gin.HandlerFunc) gin.IRoutes {
   258  	if !checkEngine() {
   259  		return nil
   260  	}
   261  	engine.Use(plugin)
   262  	return engine
   263  }
   264  
   265  func Engine() *gin.Engine {
   266  	if !checkEngine() {
   267  		return nil
   268  	}
   269  	return engine
   270  }
   271  
   272  func RegisterHealthCheckEndpoint(apiBase string) gin.IRoutes {
   273  	if "" == apiBase {
   274  		return nil
   275  	}
   276  	RegisterRoute(apiBase+"/system/status", HmAll, healthSystemStatus)
   277  	RegisterRoute(apiBase+"/system/init", HmAll, healthSystemInit)
   278  	RegisterRoute(apiBase+"/system/destroy", HmAll, healthSystemDestroy)
   279  	return engine
   280  }
   281  
   282  func RegisterConfigWatchEndpoint(apiBase string) gin.IRoutes {
   283  	if "" == apiBase {
   284  		return nil
   285  	}
   286  	RegisterRoute(apiBase+"/config/values", HmGet, config.GetConfigValues)
   287  	RegisterRoute(apiBase+"/config/values/yaml", HmGet, config.GetConfigDeepValues)
   288  	RegisterRoute(apiBase+"/config/value/:key", HmGet, config.GetConfigValue)
   289  	RegisterRoute(apiBase+"/config/update", HmPut, config.UpdateConfig)
   290  	return engine
   291  }
   292  
   293  func RegisterBeanWatchEndpoint(apiBase string) gin.IRoutes {
   294  	if "" == apiBase {
   295  		return nil
   296  	}
   297  	RegisterRoute(apiBase+"/bean/name/all", HmGet, bean.DebugBeanAll)
   298  	RegisterRoute(apiBase+"/bean/name/list/:name", HmGet, bean.DebugBeanList)
   299  	RegisterRoute(apiBase+"/bean/field/get", HmPost, bean.DebugBeanGetField)
   300  	RegisterRoute(apiBase+"/bean/field/set", HmPut, bean.DebugBeanSetField)
   301  	RegisterRoute(apiBase+"/bean/fun/call", HmPost, bean.DebugBeanFunCall)
   302  	return engine
   303  }
   304  
   305  func RegisterSwaggerEndpoint() gin.IRoutes {
   306  	RegisterRoute("/swagger/*any", HmGet, ginSwagger.WrapHandler(swaggerFiles.Handler))
   307  	return engine
   308  }
   309  
   310  func RegisterHelpEndpoint(apiBase string) gin.IRoutes {
   311  	if "" == apiBase {
   312  		return nil
   313  	}
   314  	RegisterRoute(apiBase+"/debug/help", HmGet, debug.Help)
   315  	return engine
   316  }
   317  
   318  func RegisterCustomHealthCheck(apiBase string, status func() string, init func() string, destroy func() string) gin.IRoutes {
   319  	if !checkEngine() {
   320  		return nil
   321  	}
   322  	RegisterRoute(apiBase+"/system/status", HmAll, func(c *gin.Context) {
   323  		c.Data(200, "application/json; charset=utf-8", []byte(status()))
   324  	})
   325  	RegisterRoute(apiBase+"/system/init", HmAll, func(c *gin.Context) {
   326  		c.Data(200, "application/json; charset=utf-8", []byte(init()))
   327  	})
   328  	RegisterRoute(apiBase+"/system/destroy", HmAll, func(c *gin.Context) {
   329  		c.Data(200, "application/json; charset=utf-8", []byte(destroy()))
   330  	})
   331  	return engine
   332  }
   333  
   334  func checkEngine() bool {
   335  	if engine == nil {
   336  		InitServer()
   337  		return true
   338  	}
   339  	return true
   340  }
   341  
   342  func RegisterRoute(path string, method HttpMethod, handler gin.HandlerFunc) gin.IRoutes {
   343  	if !checkEngine() {
   344  		return nil
   345  	}
   346  	if engine == nil {
   347  		logger.Warn("server启动失败,请配置 base.server.enable 或者查看相关日志")
   348  		return nil
   349  	}
   350  	switch method {
   351  	case HmAll:
   352  		engine.GET(path, handler)
   353  		engine.POST(path, handler)
   354  		engine.PUT(path, handler)
   355  		engine.DELETE(path, handler)
   356  		engine.OPTIONS(path, handler)
   357  		engine.HEAD(path, handler)
   358  	case HmGet:
   359  		engine.GET(path, handler)
   360  	case HmPost:
   361  		engine.POST(path, handler)
   362  	case HmPut:
   363  		engine.PUT(path, handler)
   364  	case HmDelete:
   365  		engine.DELETE(path, handler)
   366  	case HmOptions:
   367  		engine.OPTIONS(path, handler)
   368  	case HmHead:
   369  		engine.HEAD(path, handler)
   370  	case HmGetPost:
   371  		engine.GET(path, handler)
   372  		engine.POST(path, handler)
   373  	}
   374  	return engine
   375  }
   376  
   377  func RegisterRouteWithHeaders(path string, method HttpMethod, header []string, versionName []string, handler gin.HandlerFunc) gin.IRoutes {
   378  	if !checkEngine() {
   379  		return nil
   380  	}
   381  	p := GetApiPath(path, method)
   382  	if p == nil {
   383  		p = NewApiPath(path, method)
   384  		switch method {
   385  		case HmAll:
   386  			engine.GET(path, p.Handler)
   387  			engine.POST(path, p.Handler)
   388  			engine.PUT(path, p.Handler)
   389  			engine.DELETE(path, p.Handler)
   390  			engine.OPTIONS(path, p.Handler)
   391  			engine.HEAD(path, p.Handler)
   392  		case HmGet:
   393  			engine.GET(path, p.Handler)
   394  		case HmPost:
   395  			engine.POST(path, p.Handler)
   396  		case HmPut:
   397  			engine.PUT(path, p.Handler)
   398  		case HmDelete:
   399  			engine.DELETE(path, p.Handler)
   400  		case HmOptions:
   401  			engine.OPTIONS(path, p.Handler)
   402  		case HmHead:
   403  			engine.HEAD(path, p.Handler)
   404  		case HmGetPost:
   405  			engine.GET(path, p.Handler)
   406  			engine.POST(path, p.Handler)
   407  		}
   408  	}
   409  	p.AddVersion(header, versionName, handler)
   410  	return engine
   411  }
   412  
   413  func RegisterWebSocketRoute(path string, svr *websocket.Server) gin.IRoutes {
   414  	if !checkEngine() {
   415  		return nil
   416  	}
   417  	engine.GET(path, svr.Handler())
   418  	return engine
   419  }
   420  
   421  func Post(path string, handler gin.HandlerFunc) gin.IRoutes {
   422  	return RegisterRoute(getPathAppendApiModel(path), HmPost, handler)
   423  }
   424  
   425  func Delete(path string, handler gin.HandlerFunc) gin.IRoutes {
   426  	return RegisterRoute(getPathAppendApiModel(path), HmDelete, handler)
   427  }
   428  
   429  func Put(path string, handler gin.HandlerFunc) gin.IRoutes {
   430  	return RegisterRoute(getPathAppendApiModel(path), HmPut, handler)
   431  }
   432  
   433  func Head(path string, handler gin.HandlerFunc) gin.IRoutes {
   434  	return RegisterRoute(getPathAppendApiModel(path), HmHead, handler)
   435  }
   436  
   437  func Get(path string, handler gin.HandlerFunc) gin.IRoutes {
   438  	return RegisterRoute(getPathAppendApiModel(path), HmGet, handler)
   439  }
   440  
   441  func Options(path string, handler gin.HandlerFunc) gin.IRoutes {
   442  	return RegisterRoute(getPathAppendApiModel(path), HmOptions, handler)
   443  }
   444  
   445  func GetPost(path string, handler gin.HandlerFunc) gin.IRoutes {
   446  	return RegisterRoute(getPathAppendApiModel(path), HmGetPost, handler)
   447  }
   448  
   449  func All(path string, handler gin.HandlerFunc) gin.IRoutes {
   450  	return RegisterRoute(getPathAppendApiModel(path), HmAll, handler)
   451  }
   452  
   453  func PostWith(path string, header []string, versionName []string, handler gin.HandlerFunc) gin.IRoutes {
   454  	return RegisterRouteWithHeaders(getPathAppendApiModel(path), HmPost, header, versionName, handler)
   455  }
   456  
   457  func DeleteWith(path string, header []string, versionName []string, handler gin.HandlerFunc) gin.IRoutes {
   458  	return RegisterRouteWithHeaders(getPathAppendApiModel(path), HmDelete, header, versionName, handler)
   459  }
   460  
   461  func PutWith(path string, header []string, versionName []string, handler gin.HandlerFunc) gin.IRoutes {
   462  	return RegisterRouteWithHeaders(getPathAppendApiModel(path), HmPut, header, versionName, handler)
   463  }
   464  
   465  func HeadWith(path string, header []string, versionName []string, handler gin.HandlerFunc) gin.IRoutes {
   466  	return RegisterRouteWithHeaders(getPathAppendApiModel(path), HmHead, header, versionName, handler)
   467  }
   468  
   469  func GetWith(path string, header []string, versionName []string, handler gin.HandlerFunc) gin.IRoutes {
   470  	return RegisterRouteWithHeaders(getPathAppendApiModel(path), HmGet, header, versionName, handler)
   471  }
   472  
   473  func OptionsWith(path string, header []string, versionName []string, handler gin.HandlerFunc) gin.IRoutes {
   474  	return RegisterRouteWithHeaders(getPathAppendApiModel(path), HmOptions, header, versionName, handler)
   475  }
   476  
   477  func GetPostWith(path string, header []string, versionName []string, handler gin.HandlerFunc) gin.IRoutes {
   478  	return RegisterRouteWithHeaders(getPathAppendApiModel(path), HmGetPost, header, versionName, handler)
   479  }
   480  
   481  func AllWith(path string, header []string, versionName []string, handler gin.HandlerFunc) gin.IRoutes {
   482  	return RegisterRouteWithHeaders(getPathAppendApiModel(path), HmAll, header, versionName, handler)
   483  }
   484  
   485  func Use(middleware ...gin.HandlerFunc) gin.IRoutes {
   486  	if !checkEngine() {
   487  		return nil
   488  	}
   489  	engine.Use(middleware...)
   490  	return engine
   491  }
   492  
   493  func getPathAppendApiModel(path string) string {
   494  	// 获取 api-module
   495  	apiModel := isc.ISCString(config.GetValueString("api-module")).Trim("/")
   496  	// 获取api前缀
   497  	ap := isc.ISCString(config.GetValueStringDefault("base.api.prefix", "")).Trim("/")
   498  	if ap != "" {
   499  		ApiPrefix = "/" + string(ap)
   500  	}
   501  	p2 := isc.ISCString(path).Trim("/")
   502  	if strings.HasPrefix(string(p2), "api") {
   503  		return fmt.Sprintf("/%s", p2)
   504  	} else {
   505  		return fmt.Sprintf("/%s/%s/%s", ApiPrefix, apiModel, p2)
   506  	}
   507  }
   508  
   509  func RequestSaveHandler() gin.HandlerFunc {
   510  	return func(c *gin.Context) {
   511  		store.PutFromHead(c.Request.Header.Clone())
   512  
   513  		defer func() {
   514  			store.Clean()
   515  		}()
   516  		c.Next()
   517  	}
   518  }