Gin Web 開發腳手架技術文檔
項目概述
本項目是一個基于 Gin 框架的 Go Web 開發腳手架模板,提供了完整的項目結構、配置管理、日志記錄、MySQL 和 Redis 數據庫連接等常用功能集成。
項目結構
gindemo/
├── gindemo.exe # 編譯后的可執行文件
├── go.mod # Go 模塊定義文件
├── main.go # 應用程序入口點
├── conf/ # 配置文件目錄
│ ├── config.yaml # 主配置文件
│ └── dev.yaml # 開發環境配置文件
├── dao/ # 數據訪問層
│ ├── mysql/ # MySQL 相關
│ │ └── mysql.go
│ └── redis/ # Redis 相關
│ └── redis.go
├── logger/ # 日志模塊
│ └── logger.go
├── router/ # 路由層
│ └── route.go
└── setting/ # 配置管理└── setting.go
配置文件
name: "ginweb"
mode: "release"
port: 8080
version: "v.0.0.1"
start_time: "2025-09-01"
machine_id: 1log:level: "info"filename: "web_app.log"max_size: 200max_age: 30max_backups: 7mysql:host: 127.0.0.1port: 13306dbname: "ginweb"user: "root"password: "root"max_open_conns: 200max_idle_conns: 50redis:host: 127.0.0.1port: 6379password: ""db: 0pool_size: 100
核心模塊詳解
1. 應用程序入口 (main.go)
package mainimport ("fmt""gindemo/dao/mysql""gindemo/dao/redis""gindemo/logger""gindemo/router""gindemo/setting""os"
)// Go Web 開發通用的腳手架模板
func main() {if len(os.Args) < 2 {fmt.Printf("need config file.eg: conf config.yaml")return}fmt.Println("load config file:", os.Args[1])// 加載配置if err := setting.Init(os.Args[1]); err != nil {fmt.Printf("load config failed, err:%v\n", err)return}//日志if err := logger.Init(setting.Conf.LogConfig, setting.Conf.Mode); err != nil {fmt.Printf("init logger failed, err:%v\n", err)return}// mysqlif err := mysql.Init(setting.Conf.MysqlConfig); err != nil {fmt.Printf("init mysql failed, err:%v\n", err)return}defer mysql.Close()// redisif err := redis.Init(setting.Conf.RedisConfig); err != nil {fmt.Printf("init redis failed, err:%v\n", err)return}defer redis.Close()//注冊路由r := router.SetupRouter(setting.Conf.Mode)err := r.Run(fmt.Sprintf(":%d", setting.Conf.Port))if err != nil {fmt.Printf("start server failed, err:%v\n", err)return}
}
功能特點:
- 命令行參數驗證,要求指定配置文件路徑
- 模塊化初始化流程:配置 → 日志 → MySQL → Redis → 路由
- 完善的錯誤處理和資源清理(defer 關閉數據庫連接)
2. 配置管理模塊 (setting.go)
package settingimport ("fmt""github.com/fsnotify/fsnotify""github.com/spf13/viper"
)type AppConfig struct {Name string `mapstructure:"name,omitempty"`Mode string `mapstructure:"mode,omitempty"`Version string `mapstructure:"version,omitempty"`StartTime string `mapstructure:"start_time,omitempty"`machineId string `mapstructure:"machine_id,omitempty"`Port int `mapstructure:"port,omitempty"`*LogConfig `mapstructure:"log,omitempty"`*MysqlConfig `mapstructure:"mysql,omitempty"`*RedisConfig `mapstructure:"redis,omitempty"`
}type LogConfig struct {Level string `mapstructure:"level,omitempty"`Filename string `mapstructure:"filename,omitempty"`MaxSize int `mapstructure:"max_size,omitempty"`MaxAge int `mapstructure:"max_age,omitempty"`MaxBackups int `mapstructure:"max_backups,omitempty"`
}
type MysqlConfig struct {Host string `mapstructure:"host,omitempty"`Port int `mapstructure:"port,omitempty"`DB string `mapstructure:"dbname,omitempty"`User string `mapstructure:"user,omitempty"`Password string `mapstructure:"password,omitempty"`MaxOpenConns int `mapstructure:"max_open_conns,omitempty"`MaxIdleConns int `mapstructure:"max_idle_conns,omitempty"`
}type RedisConfig struct {Host string `mapstructure:"host,omitempty"`Port int `mapstructure:"port,omitempty"`Password string `mapstructure:"password,omitempty"`DB int `mapstructure:"db,omitempty"`PoolSize int `mapstructure:"pool_size,omitempty"`MinIdleConns int `mapstructure:"min_idle_conns,omitempty"`
}var Conf = new(AppConfig)func Init(filePath string) (err error) {viper.SetConfigFile(filePath)//讀取配置信息err = viper.ReadInConfig()if err != nil {fmt.Printf("viper.ReadInConfig() failed, err:%v\n", err)return}//把讀取到的配置信息反序列化到 Conf 變量中if err := viper.Unmarshal(Conf); err != nil {fmt.Printf("viper.Unmarshal() failed, err:%v\n", err)}viper.WatchConfig()viper.OnConfigChange(func(e fsnotify.Event) {fmt.Printf("Config file changed, filename:%s\n", e.Name)if err2 := viper.Unmarshal(Conf); err2 != nil {fmt.Printf("viper.Unmarshal() failed, err:%v\n", err2)}})return
}
功能特點:
- 使用 viper 庫實現 YAML 配置文件的讀取和解析
- 支持配置熱加載,文件變更時自動重新加載配置
- 結構化的配置定義,類型安全
3. 日志模塊 (logger.go)
package loggerimport ("gindemo/setting""net""net/http""net/http/httputil""os""runtime/debug""strings""time""github.com/gin-gonic/gin""go.uber.org/zap""go.uber.org/zap/zapcore""gopkg.in/natefinch/lumberjack.v2"
)var lg *zap.Logger// Init 初始化lg
func Init(cfg *setting.LogConfig, mode string) (err error) {writeSyncer := getLogWriter(cfg.Filename, cfg.MaxSize, cfg.MaxBackups, cfg.MaxAge)encoder := getEncoder()var l = new(zapcore.Level)err = l.UnmarshalText([]byte(cfg.Level))if err != nil {return}var core zapcore.Coreif mode == "dev" {// 進入開發模式,日志輸出到終端consoleEncoder := zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig())core = zapcore.NewTee(zapcore.NewCore(encoder, writeSyncer, l),zapcore.NewCore(consoleEncoder, zapcore.Lock(os.Stdout), zapcore.DebugLevel),)} else {core = zapcore.NewCore(encoder, writeSyncer, l)}lg = zap.New(core, zap.AddCaller())zap.ReplaceGlobals(lg)zap.L().Info("init logger success")return
}func getEncoder() zapcore.Encoder {encoderConfig := zap.NewProductionEncoderConfig()encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoderencoderConfig.TimeKey = "time"encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoderencoderConfig.EncodeDuration = zapcore.SecondsDurationEncoderencoderConfig.EncodeCaller = zapcore.ShortCallerEncoderreturn zapcore.NewJSONEncoder(encoderConfig)
}func getLogWriter(filename string, maxSize, maxBackup, maxAge int) zapcore.WriteSyncer {lumberJackLogger := &lumberjack.Logger{Filename: filename,MaxSize: maxSize,MaxBackups: maxBackup,MaxAge: maxAge,}return zapcore.AddSync(lumberJackLogger)
}// GinLogger 接收gin框架默認的日志
func GinLogger() gin.HandlerFunc {return func(c *gin.Context) {start := time.Now()path := c.Request.URL.Pathquery := c.Request.URL.RawQueryc.Next()cost := time.Since(start)lg.Info(path,zap.Int("status", c.Writer.Status()),zap.String("method", c.Request.Method),zap.String("path", path),zap.String("query", query),zap.String("ip", c.ClientIP()),zap.String("user-agent", c.Request.UserAgent()),zap.String("errors", c.Errors.ByType(gin.ErrorTypePrivate).String()),zap.Duration("cost", cost),)}
}// GinRecovery recover掉項目可能出現的panic,并使用zap記錄相關日志
func GinRecovery(stack bool) gin.HandlerFunc {return func(c *gin.Context) {defer func() {if err := recover(); err != nil {// Check for a broken connection, as it is not really a// condition that warrants a panic stack trace.var brokenPipe boolif ne, ok := err.(*net.OpError); ok {if se, ok := ne.Err.(*os.SyscallError); ok {if strings.Contains(strings.ToLower(se.Error()), "broken pipe") || strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") {brokenPipe = true}}}httpRequest, _ := httputil.DumpRequest(c.Request, false)if brokenPipe {lg.Error(c.Request.URL.Path,zap.Any("error", err),zap.String("request", string(httpRequest)),)// If the connection is dead, we can't write a status to it.c.Error(err.(error)) // nolint: errcheckc.Abort()return}if stack {lg.Error("[Recovery from panic]",zap.Any("error", err),zap.String("request", string(httpRequest)),zap.String("stack", string(debug.Stack())),)} else {lg.Error("[Recovery from panic]",zap.Any("error", err),zap.String("request", string(httpRequest)),)}c.AbortWithStatus(http.StatusInternalServerError)}}()c.Next()}
}
功能特點:
- 基于 zap 高性能日志庫
- 支持日志輪轉(lumberjack)
- 開發模式同時輸出到文件和控制臺
- 提供 Gin 框架的日志中間件和異常恢復處理
- 智能處理 broken pipe 等連接異常
4. MySQL 數據訪問模塊 (mysql.go)
package mysqlimport ("fmt""gindemo/setting"_ "github.com/go-sql-driver/mysql""github.com/jmoiron/sqlx"
)var db *sqlx.DBfunc Init(cfg *setting.MysqlConfig) (err error) {dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?parseTime=true&loc=Local", cfg.User, cfg.Password, cfg.Host, cfg.Port, cfg.DB)db, err = sqlx.Connect("mysql", dsn)if err != nil {return}db.SetMaxIdleConns(cfg.MaxIdleConns)db.SetMaxOpenConns(cfg.MaxOpenConns)return
}func Close() {_ = db.Close()
}
功能特點:
- 使用 sqlx 增強的 MySQL 驅動
- 支持連接池配置
- 自動處理時區問題(loc=Local)
5. Redis 數據訪問模塊 (redis.go)
package redisimport ("fmt""gindemo/setting""github.com/go-redis/redis"
)var (client *redis.ClientNil = redis.Nil
)func Init(cfg *setting.RedisConfig) (err error) {client = redis.NewClient(&redis.Options{Addr: fmt.Sprintf("%s:%d", cfg.Host, cfg.Port),Password: cfg.Password,DB: cfg.DB,PoolSize: cfg.PoolSize,MinIdleConns: cfg.MinIdleConns,})_, err = client.Ping().Result()if err != nil {return err}return nil
}func Close() {_ = client.Close()
}
功能特點:
- 使用 go-redis 客戶端庫
- 支持連接池配置
- 導出 Nil 錯誤常量便于處理鍵不存在的情況
6. 路由管理模塊 (route.go)
package routerimport ("gindemo/logger""net/http""github.com/gin-gonic/gin"
)func SetupRouter(mode string) *gin.Engine {if mode == gin.ReleaseMode {// gin 設置成發布模式gin.SetMode(gin.ReleaseMode)}router := gin.New()router.Use(logger.GinLogger(), logger.GinRecovery(true))router.GET("/ping", func(c *gin.Context) {c.String(http.StatusOK, "pong")})return router
}
功能特點:
- 根據運行模式自動設置 Gin 模式
- 集成 zap 日志中間件和異常恢復
- 提供健康檢查端點 /ping
啟動和運行
編譯項目
go build -o gindemo.exe main.go
運行項目
./gindemo.exe conf/config.yaml
測試服務
curl http://localhost:8080/ping
配置說明
應用配置
name
: 應用名稱mode
: 運行模式(debug/release/test)port
: HTTP 服務監聽端口version
: 應用版本號start_time
: 啟動時間標識machine_id
: 機器標識,用于分布式部署
日志配置
level
: 日志級別(debug/info/warn/error)filename
: 日志文件名max_size
: 單個日志文件最大大小(MB)max_age
: 日志保留天數max_backups
: 最大備份文件數
MySQL 配置
- 連接參數:主機、端口、數據庫名、用戶名、密碼
- 連接池:最大打開連接數、最大空閑連接數
Redis 配置
- 連接參數:主機、端口、密碼、數據庫編號
- 連接池:連接池大小、最小空閑連接數
擴展開發指南
添加新的配置項
- 在
setting.go
的對應配置結構體中添加字段 - 在配置文件中添加相應的配置項
- 在需要使用的地方通過
setting.Conf
訪問
添加新的路由
在 router.go
的 SetupRouter
函數中添加新的路由定義:
router.GET("/new-endpoint", func(c *gin.Context) {// 處理邏輯
})
添加業務模塊
- 創建新的包目錄
- 實現相關功能
- 在
main.go
中初始化和集成
總結
這個 Gin Web 開發腳手架提供了一個結構清晰、功能完整的起點,具有以下特點:
- 模塊化設計:各功能模塊職責清晰,便于維護和擴展
- 配置驅動:所有可變參數都通過配置文件管理
- 生產就緒:包含日志、監控、異常處理等生產環境必需功能
- 高性能:基于 Gin 和 zap 等高性能庫
- 易于擴展:清晰的架構便于添加新功能
該項目適合作為中小型 Web 服務的開發基礎,可根據具體需求進行進一步定制和擴展。