package main import ( "context" "log" "net/http" "os" "os/signal" "syscall" "time" "github.com/edu-cloud/push-gateway/internal/config" "github.com/edu-cloud/push-gateway/internal/hub" "github.com/edu-cloud/push-gateway/internal/observability" "github.com/edu-cloud/push-gateway/internal/ws" "github.com/gin-gonic/gin" "go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin" ) func main() { cfg := config.Load() // 初始化 OpenTelemetry tracer(endpoint 为空时自动跳过) tracerShutdown := observability.InitTracer("push-gateway", cfg.OTLPEndpoint) defer tracerShutdown() gin.SetMode(gin.ReleaseMode) h := hub.NewHub() wsHandler := ws.NewHandler(h, cfg.JWTSecret, cfg.DevMode) r := gin.New() r.Use(gin.Recovery()) // OpenTelemetry 自动埋点(HTTP 请求/响应 span) r.Use(otelgin.Middleware("push-gateway")) r.GET("/healthz", func(c *gin.Context) { c.JSON(200, gin.H{"status": "ok", "service": "push-gateway"}) }) // WebSocket 升级端点 r.GET("/ws", wsHandler.HandleWebSocket) // 内部推送 API(Msg 服务调用) api := r.Group("/internal") api.POST("/push", wsHandler.PushHandler) api.POST("/broadcast", wsHandler.BroadcastHandler) srv := &http.Server{ Addr: ":" + cfg.Port, Handler: r, ReadTimeout: 10 * time.Second, WriteTimeout: 60 * time.Second, // WebSocket 长连接 } go func() { log.Printf("Push Gateway listening on :%s", cfg.Port) if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatalf("listen: %s\n", err) } }() quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) <-quit log.Println("Shutting down Push Gateway...") ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err := srv.Shutdown(ctx); err != nil { log.Fatal("Server forced to shutdown:", err) } }