package bootstraps import ( "context" "fmt" "log" "net/http" "os" "os/signal" "syscall" "time" "git.x2erp.com/qdy/go-base/config" "git.x2erp.com/qdy/go-base/logger" ) // 定义接口 type ShutdownHandler interface { OnShutdown() } // Bootstrapper 服务启动器 type Bootstrapper struct { serviceName string serviceVersion string Cfg config.IConfig HttpServer *http.Server Router *http.ServeMux quit chan os.Signal } // New 创建启动器 func New(name, version string) *Bootstrapper { b := &Bootstrapper{ serviceName: name, serviceVersion: version, quit: make(chan os.Signal, 1), Router: http.NewServeMux(), // 直接创建路由器 } return b } // Init 初始化配置和日志 func (b *Bootstrapper) Init() *Bootstrapper { // 1. 初始化启动日志 if err := logger.InitBootLog(b.serviceName); err != nil { log.Fatal("无法初始化启动日志: ", err) } // 2. 加载配置 log.Println("正在加载配置...") cfg, err := config.GetConfig() if err != nil { log.Fatalf("加载配置失败: %v", err) } cfg.SetServiceName(b.serviceName) cfg.SetServiceVersion(b.serviceVersion) b.Cfg = cfg // 3. 初始化运行时日志 logger.InitRuntimeLogger(b.serviceName, b.serviceVersion, b.Cfg.GetLog()) log.Println("配置和日志初始化完成") return b } // StartService 启动HTTP服务 func (b *Bootstrapper) StartService() *Bootstrapper { serviceCfg := b.Cfg.GetService() // 创建HTTP服务器 b.HttpServer = &http.Server{ Addr: fmt.Sprintf(":%d", serviceCfg.Port), Handler: b.Router, // 直接使用Router ReadTimeout: 15 * time.Second, WriteTimeout: 15 * time.Second, IdleTimeout: 60 * time.Second, } log.Printf("正在启动服务: %s v%s", b.serviceName, b.serviceVersion) log.Printf("模式: 独立运行 (net/http)") log.Printf("服务端口: :%d", serviceCfg.Port) return b } // Run 运行服务 func (b *Bootstrapper) Run(handler ShutdownHandler) { log.Printf("服务 %s 开始运行...", b.serviceName) // 设置信号监听 - 在这里设置,而不是在New中 signal.Notify(b.quit, syscall.SIGINT, syscall.SIGTERM) // 启动HTTP服务器 go func() { log.Printf("服务器启动在 %s", b.HttpServer.Addr) if err := b.HttpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatalf("服务运行失败: %v", err) } }() // 等待中断信号 b.waitForShutdown(handler) } // RunTLS 运行HTTPS服务 func (b *Bootstrapper) RunTLS(certFile, keyFile string, handler ShutdownHandler) { log.Printf("服务 %s 开始运行(HTTPS)...", b.serviceName) // 设置信号监听 signal.Notify(b.quit, syscall.SIGINT, syscall.SIGTERM) // 启动HTTPS服务器 go func() { log.Printf("HTTPS服务器启动在 %s", b.HttpServer.Addr) if err := b.HttpServer.ListenAndServeTLS(certFile, keyFile); err != nil && err != http.ErrServerClosed { log.Fatalf("服务运行失败: %v", err) } }() // 等待中断信号 b.waitForShutdown(handler) } // waitForShutdown 等待关闭信号 func (b *Bootstrapper) waitForShutdown(handler ShutdownHandler) { log.Println("按 Ctrl+C 停止服务") // 等待信号 <-b.quit log.Println("接收到终止信号,正在优雅关闭服务...") // 创建关闭上下文,给30秒完成当前请求 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() // 停止接收新请求,完成当前请求 if err := b.HttpServer.Shutdown(ctx); err != nil { log.Printf("服务关闭失败: %v", err) } else { log.Println("HTTP服务器已关闭") } // 执行关闭处理 if handler != nil { handler.OnShutdown() } // 停止日志写入 logger.StopESWriter() log.Println("服务优雅关闭完成") // 等待一小段时间确保日志写入完成 time.Sleep(100 * time.Millisecond) os.Exit(0) } // GetConfig 获取配置 func (b *Bootstrapper) GetConfig() config.IConfig { return b.Cfg } // GetRouter 获取路由器 func (b *Bootstrapper) GetRouter() *http.ServeMux { return b.Router } // 兼容性方法 // GetWebService 获取Web服务(兼容性方法) func (b *Bootstrapper) GetWebService() interface{} { return b.Router } // Handle 注册路由处理器(兼容性方法) func (b *Bootstrapper) Handle(pattern string, handler http.Handler) { b.Router.Handle(pattern, handler) }