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 }