2025-06-25 13:26:35 +08:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"os/signal"
|
|
|
|
"syscall"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"github.com/joho/godotenv"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
)
|
|
|
|
|
|
|
|
var logger *zap.Logger
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
// 初始化日志
|
|
|
|
var err error
|
|
|
|
logger, err = zap.NewProduction()
|
|
|
|
if err != nil {
|
|
|
|
panic(fmt.Sprintf("Failed to initialize logger: %v", err))
|
|
|
|
}
|
|
|
|
defer logger.Sync()
|
|
|
|
|
|
|
|
// 加载环境变量
|
|
|
|
if err := godotenv.Load(); err != nil {
|
|
|
|
logger.Warn("No .env file found, using system environment variables")
|
|
|
|
}
|
|
|
|
|
|
|
|
// 设置Gin模式
|
|
|
|
mode := getEnv("GIN_MODE", "debug")
|
|
|
|
gin.SetMode(mode)
|
|
|
|
|
|
|
|
// 创建路由
|
|
|
|
router := setupRouter()
|
|
|
|
|
|
|
|
// 获取端口
|
|
|
|
port := getEnv("PORT", "8080")
|
2025-06-25 15:12:40 +08:00
|
|
|
|
2025-06-25 13:26:35 +08:00
|
|
|
// 创建HTTP服务器
|
|
|
|
srv := &http.Server{
|
|
|
|
Addr: ":" + port,
|
|
|
|
Handler: router,
|
|
|
|
ReadTimeout: 10 * time.Second,
|
|
|
|
WriteTimeout: 10 * time.Second,
|
|
|
|
IdleTimeout: 60 * time.Second,
|
|
|
|
}
|
|
|
|
|
|
|
|
// 在单独的goroutine中启动服务器
|
|
|
|
go func() {
|
|
|
|
logger.Info("Starting server", zap.String("port", port), zap.String("mode", mode))
|
|
|
|
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
|
|
|
logger.Fatal("Failed to start server", zap.Error(err))
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
// 等待中断信号来优雅地关闭服务器
|
|
|
|
quit := make(chan os.Signal, 1)
|
|
|
|
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
|
|
|
<-quit
|
|
|
|
logger.Info("Shutting down server...")
|
|
|
|
|
|
|
|
// 给服务器5秒钟来完成正在处理的请求
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
if err := srv.Shutdown(ctx); err != nil {
|
|
|
|
logger.Fatal("Server forced to shutdown", zap.Error(err))
|
|
|
|
}
|
|
|
|
|
|
|
|
logger.Info("Server exited")
|
|
|
|
}
|
|
|
|
|
|
|
|
func setupRouter() *gin.Engine {
|
|
|
|
router := gin.New()
|
|
|
|
|
|
|
|
// 使用自定义中间件
|
|
|
|
router.Use(ginZapLogger(logger), ginZapRecovery(logger, true))
|
|
|
|
|
|
|
|
// 基本路由
|
|
|
|
router.GET("/", handleHome)
|
|
|
|
router.GET("/ping", handlePing)
|
|
|
|
router.GET("/health", handleHealth)
|
|
|
|
router.GET("/version", handleVersion)
|
|
|
|
|
|
|
|
// API组
|
|
|
|
api := router.Group("/api/v1")
|
|
|
|
{
|
|
|
|
api.GET("/status", handleAPIStatus)
|
|
|
|
api.GET("/time", handleTime)
|
|
|
|
api.POST("/echo", handleEcho)
|
|
|
|
}
|
|
|
|
|
|
|
|
return router
|
|
|
|
}
|
|
|
|
|
|
|
|
// 处理函数
|
|
|
|
func handleHome(c *gin.Context) {
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
"message": "Welcome to Golang Demo API",
|
|
|
|
"version": "1.0.0",
|
|
|
|
"environment": getEnv("GIN_MODE", "development"),
|
|
|
|
"timestamp": time.Now().Format(time.RFC3339),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func handlePing(c *gin.Context) {
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
"message": "pong",
|
|
|
|
"time": time.Now().Unix(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func handleHealth(c *gin.Context) {
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
"status": "healthy",
|
|
|
|
"timestamp": time.Now().Format(time.RFC3339),
|
|
|
|
"uptime": time.Since(startTime).String(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func handleVersion(c *gin.Context) {
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
"version": "1.0.0",
|
|
|
|
"build_time": buildTime,
|
|
|
|
"go_version": "1.21+",
|
|
|
|
"git_commit": gitCommit,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func handleAPIStatus(c *gin.Context) {
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
"api_version": "v1",
|
|
|
|
"status": "active",
|
|
|
|
"endpoints": []string{
|
|
|
|
"GET /api/v1/status",
|
|
|
|
"GET /api/v1/time",
|
|
|
|
"POST /api/v1/echo",
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func handleTime(c *gin.Context) {
|
|
|
|
now := time.Now()
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
"timestamp": now.Unix(),
|
|
|
|
"iso": now.Format(time.RFC3339),
|
|
|
|
"utc": now.UTC().Format(time.RFC3339),
|
|
|
|
"timezone": now.Location().String(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func handleEcho(c *gin.Context) {
|
|
|
|
var body map[string]interface{}
|
|
|
|
if err := c.ShouldBindJSON(&body); err != nil {
|
|
|
|
c.JSON(http.StatusBadRequest, gin.H{
|
|
|
|
"error": "Invalid JSON",
|
|
|
|
"message": err.Error(),
|
|
|
|
})
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
|
|
"echo": body,
|
|
|
|
"timestamp": time.Now().Format(time.RFC3339),
|
|
|
|
"client_ip": c.ClientIP(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// 工具函数
|
|
|
|
func getEnv(key, defaultValue string) string {
|
|
|
|
if value := os.Getenv(key); value != "" {
|
|
|
|
return value
|
|
|
|
}
|
|
|
|
return defaultValue
|
|
|
|
}
|
|
|
|
|
|
|
|
// 构建时注入的变量
|
|
|
|
var (
|
|
|
|
startTime = time.Now()
|
|
|
|
buildTime = "unknown"
|
|
|
|
gitCommit = "unknown"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Gin中间件
|
|
|
|
func ginZapLogger(logger *zap.Logger) gin.HandlerFunc {
|
|
|
|
return gin.LoggerWithWriter(gin.DefaultWriter)
|
|
|
|
}
|
|
|
|
|
|
|
|
func ginZapRecovery(logger *zap.Logger, stack bool) gin.HandlerFunc {
|
|
|
|
return gin.Recovery()
|
2025-06-25 15:12:40 +08:00
|
|
|
}
|