package webx import ( "fmt" "log" "net/http" "os" "os/signal" "sync" "syscall" "time" "git.x2erp.com/qdy/go-base/client" "git.x2erp.com/qdy/go-base/config/subconfigs" "git.x2erp.com/qdy/go-base/logger" ) // WebService Web服务实例 type WebService struct { serviceName string httpServer *http.Server router *http.ServeMux //RouterService *router.RouterService // 暴露RouterService quit chan os.Signal config *subconfigs.ServiceConfig ip string port int } // WebServiceFactory Web服务工厂(全局单例模式) type WebServiceFactory struct { mu sync.RWMutex webService *WebService } var ( instanceWebService *WebServiceFactory instanceWebServiceOnce sync.Once ) // GetWebServiceFactory 获取Web服务工厂单例 func GetWebServiceFactory() *WebServiceFactory { instanceWebServiceOnce.Do(func() { log.Printf("Creating WebServiceFactory...") instanceWebService = &WebServiceFactory{} log.Printf("WebServiceFactory is successfully created.") }) return instanceWebService } // CreateService 创建Web服务实例 func (f *WebServiceFactory) CreateService(config *subconfigs.ServiceConfig) (*WebService, error) { f.mu.Lock() defer f.mu.Unlock() // 检查是否已存在服务 if f.webService != nil { log.Printf("服务已存在,返回现有实例: %s", f.webService.serviceName) return f.webService, nil } // 验证配置 if config == nil { return nil, fmt.Errorf("服务配置不能为空") } // 设置默认值 if config.Port == 0 { config.Port = 8080 } if config.ServiceName == "" { config.ServiceName = "default-service" } if config.ReadTimeout == 0 { config.ReadTimeout = 15 } if config.WriteTimeout == 0 { config.WriteTimeout = 15 } if config.IdleTimeout == 0 { config.IdleTimeout = 60 } // 获取IP ip := client.GetServiceIP("") // 创建服务实例 service := &WebService{ serviceName: config.ServiceName, router: http.NewServeMux(), quit: make(chan os.Signal, 1), config: config, ip: ip, port: config.Port, } // 创建HTTP服务器 service.httpServer = &http.Server{ Addr: fmt.Sprintf(":%d", config.Port), Handler: service.router, ReadTimeout: time.Duration(config.ReadTimeout) * time.Second, WriteTimeout: time.Duration(config.WriteTimeout) * time.Second, IdleTimeout: time.Duration(config.IdleTimeout) * time.Second, } // 保存实例 f.webService = service log.Printf("已创建服务实例: %s (端口: %d)", config.ServiceName, config.Port) return service, nil } // IsCreated 检查服务是否已创建 func (f *WebServiceFactory) IsCreated() bool { f.mu.RLock() defer f.mu.RUnlock() return f.webService != nil } // ========== WebService 实例方法 ========== // GetRouter 获取路由器 func (s *WebService) GetRouter() *http.ServeMux { return s.router } // GetServer 获取HTTP服务器 func (s *WebService) GetServer() *http.Server { return s.httpServer } // GetServiceName 获取服务名称 func (s *WebService) GetServiceName() string { return s.serviceName } // GetIP 获取服务IP func (s *WebService) GetIP() string { return s.ip } // GetPort 获取服务端口 func (s *WebService) GetPort() int { return s.port } // GetConfig 获取服务配置 func (s *WebService) GetConfig() *subconfigs.ServiceConfig { return s.config } // RegisterRoute 注册路由 func (s *WebService) RegisterRoute(pattern string, handler http.Handler) { s.router.Handle(pattern, handler) } // RegisterRouteFunc 注册路由处理函数 func (s *WebService) RegisterRouteFunc(pattern string, handler func(http.ResponseWriter, *http.Request)) { s.router.HandleFunc(pattern, handler) } // Run 运行所有服务 func (s *WebService) Run() { // 设置全局信号监听 signal.Notify(s.quit, syscall.SIGINT, syscall.SIGTERM) log.Printf("启动服务 %s 在 %s", s.serviceName, s.httpServer.Addr) // 在goroutine中启动服务,避免阻塞 go func() { if err := s.httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatalf("服务 %s 运行失败: %v", s.serviceName, err) } }() // 等待中断信号,传递指针 //s.waitForServiceShutdown(containerFactory) } // RunTLS 运行HTTPS服务 func (s *WebService) RunTLS(serviceName, certFile, keyFile string) { log.Printf("服务 %s 开始运行(HTTPS)...", s.serviceName) // 设置服务级别的信号监听 signal.Notify(s.quit, syscall.SIGINT, syscall.SIGTERM) // 启动HTTPS服务器 go func() { log.Printf("HTTPS服务器启动在 %s", s.httpServer.Addr) if err := s.httpServer.ListenAndServeTLS(certFile, keyFile); err != nil && err != http.ErrServerClosed { log.Fatalf("服务运行失败: %v", err) } }() // 等待中断信号 //s.waitForServiceShutdown(containerFactory) } // // waitForServiceShutdown 等待单个服务关闭 // func (s *WebService) WaitForServiceShutdown(containerFactory *container.ContainerFactory) { // log.Printf("按 Ctrl+C 停止服务 %s", s.serviceName) // // 等待信号 // <-s.quit // log.Printf("接收到终止信号,正在优雅关闭服务 %s...", s.serviceName) // //退出注册中心 // //consul.Deregister(s.serviceName, s.Ip, s.Port, b.Cfg.GetConsulConfig()) // // 创建关闭上下文,给30秒完成当前请求 // ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) // defer cancel() // // 停止接收新请求,完成当前请求 // if err := s.httpServer.Shutdown(ctx); err != nil { // log.Printf("服务 %s 关闭失败: %v", s.serviceName, err) // } else { // log.Printf("服务 %s 已关闭", s.serviceName) // } // // 执行关闭处理 // if containerFactory != nil { // containerFactory.CloseAll() // } // // 停止日志写入 // logger.StopESWriter() // log.Printf("服务 %s 优雅关闭完成", s.serviceName) // // 等待一小段时间确保日志写入完成 // time.Sleep(100 * time.Millisecond) // os.Exit(0) // } // AddMiddleware 添加中间件 func (s *WebService) AddMiddleware(middleware func(http.Handler) http.Handler) { s.httpServer.Handler = middleware(s.httpServer.Handler) } // EnableCORS 启用CORS支持 func (s *WebService) EnableCORS() { s.AddMiddleware(func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization") if r.Method == "OPTIONS" { w.WriteHeader(http.StatusOK) return } next.ServeHTTP(w, r) }) }) } // EnableRequestLogging 启用请求日志记录 func (s *WebService) EnableRequestLogging() { s.AddMiddleware(func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { start := time.Now() // 使用包装器记录响应状态码 rw := &responseWriter{ResponseWriter: w, statusCode: http.StatusOK} next.ServeHTTP(rw, r) // 记录日志 duration := time.Since(start) log.Printf("%s %s %d %v", r.Method, r.URL.Path, rw.statusCode, duration) logger.Info("%s %s %d %v", r.Method, r.URL.Path, rw.statusCode, duration) }) }) } // 辅助类型,用于记录响应状态码 type responseWriter struct { http.ResponseWriter statusCode int } func (rw *responseWriter) WriteHeader(code int) { rw.statusCode = code rw.ResponseWriter.WriteHeader(code) } // HealthCheck 添加健康检查端点 func (s *WebService) HealthCheck(pattern string) { s.RegisterRouteFunc(pattern, func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write([]byte("OK")) }) } // Metrics 添加指标端点 func (s *WebService) Metrics(pattern string) { s.RegisterRouteFunc(pattern, func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/plain") w.WriteHeader(http.StatusOK) // 这里可以添加应用的指标数据 w.Write([]byte("# HELP go_info Information about the Go environment.\n")) w.Write([]byte("# TYPE go_info gauge\n")) w.Write([]byte("go_info{version=\"1.19\"} 1\n")) }) } // StaticFile 添加静态文件服务 func (s *WebService) StaticFile(pattern, filepath string) { s.RegisterRouteFunc(pattern, func(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, filepath) }) } // StaticDirectory 添加静态目录服务 func (s *WebService) StaticDirectory(pattern, directory string) { s.RegisterRoute(pattern, http.StripPrefix(pattern, http.FileServer(http.Dir(directory)))) }