使用手册 架构 部署 FAQ

ThinkGin 3.14.1 文档

一站式 Go Web 框架参考手册。按"入门 → 核心 → 开发与部署"的顺序,讲清楚如何把 ThinkGin 装起来,又如何把它稳定交付到生产环境。

项目介绍

ThinkGin 3.14.1 是一个基于 Gin 1.12 构建的生产级 Go Web 框架,定位为单体业务后台脚手架,强调工程纪律 · 配置秩序 · 开箱即交付。近期版本完成 P0 安全加固(JWT 密钥外置 / CORS 安全默认 / Session 强制 HttpOnly / Redis Flush 限定前缀 / metrics 鉴权姿态)、P1 正确性修复(熔断器并发与低流量、缓存击穿 singleflight、内存缓存 goroutine 泄漏)、P2 工程质量打磨(CLI 测试补齐、迁移文档统一、loader 去重)、P3 交付能力(完整业务模块范例 article、生产级 Dockerfile)与长连接健壮性增强(WebSocket 心跳保活、config 命令脱敏、心跳停止并发修复), 自定义中间件注册表route.RegisterMiddleware),业务可无需 fork 即可按名注入或覆盖任何内置中间件; 同时保留安全加固(CSRF/JWT/CORS)、并发安全(atomic.Pointer 配置热更新)、路由级熔断器、lumberjack 日志轮转等能力。

⚙️ 工程纪律

三平台 CI + -race 强制门禁,lint + 70%+ 测试覆盖,面向生产的工程品质。

🔧 模块化

完全模块化的 YAML 配置管理。

📊 可观测

日志、监控、链路追踪能力分层落地。

🛡️ 企业级

生产就绪的安全性与稳定性设计。

🎨 易用性

简洁的 API 表达与清晰的文档组织。

🌐 多场景

支持 Web、API、微服务等多种形态。

核心特性

特性 描述
MVC 架构 标准的 Model-View-Controller 分层,配合 cmd/scaffold 一键生成模块骨架
模块化配置 13 个独立 YAML,THINKGIN_* 前缀环境变量覆盖,默认不预置任何真实依赖
GORM 多数据源 MySQL / PostgreSQL / SQLite,GORM OTel 插件把 SQL 纳入同一条 HTTP trace
Redis 多实例 redis/go-redis v9,启动期 Ping 探活;可复用为 Session 后端
Session 运行时 memory / redis 双 driver,Cookie 安全标志自动透传
JWT 鉴权 HS256 签发与校验,严格拒绝 alg=none 伪造
Prometheus HTTP 四维 + 运行时 + 业务指标,分模块启停
链路追踪 OpenTelemetry TracerProvider;可通过 OTLP 上报 Jaeger / Tempo / Zipkin
K8s 探针 /livez 仅验证进程存活;/readyz 并发 Ping DB / Redis,降级返 503
优雅停机 信号监听 + 超时 + DB / Cache / Tracer 依序释放
Logger 接口 抽象 Logger 接口,内置 logrus + slog 双适配器,WithFields 线程安全
HTTPS 双监听 配置 server.https.enabled=true 后 HTTP/HTTPS 并发启动,TLS 1.2+
ServiceContext 聚合 Config/Logger/DB/Cache,通过 SvcFromGin(c) 请求链路获取,替代全局单例
安全中间件 CSRF 双重提交 Cookie + Gzip 压缩 + 安全响应头(X-Frame-Options 等),middleware.global 按名启用
Swagger / OpenAPI 零 Go 依赖 Swagger UI,维护 docs/openapi.yaml 即可在 /swagger/ 预览
数据库迁移 GORM Migrator:Register → Migrate → Rollback → Status,Go 函数注册,类型安全
配置热更新 fsnotify 监听 YAML 变更,去抖 500ms,回调通知业务层刷新运行时状态
Redis 分布式限流 Lua 脚本原子令牌桶,Redis 故障自动降级为 no-op
i18n 运行时 YAML 翻译文件 + Accept-Language / ?lang= 检测 + T(c, key)

快速开始

用几条命令把服务跑起来,是评估一个框架最直接的方式。

环境要求

  • Go 版本:1.25 或更高。Gin 1.12 通过 quic-go 间接要求 Go 1.25+
  • 操作系统:Windows 10+ / macOS 10.14+ / Linux(Ubuntu 20.04+, CentOS 7+)
  • 内存:最低 512MB,推荐 1GB+
  • 磁盘空间:最低 100MB

从源码运行

# 源码仓库:Gitee (国内) 或 GitHub (国外)
git clone https://gitee.com/libaicode/thinkgin.git
# git clone https://github.com/thinkgin/thinkgin.git
cd thinkgin

go mod tidy
go run main.go

# 验证运行
curl http://localhost:8000/

编译后运行

# 编译项目
go build -o thinkgin main.go

# Linux / macOS
./thinkgin

# Windows
thinkgin.exe

Docker 运行

docker build -t thinkgin:latest .
docker run -p 8000:8000 thinkgin:latest
Tip · 浏览器打开 http://localhost:8000/,或调用接口 http://localhost:8000/index/hello;若启用了 Prometheus,则 /metrics 可直接查看指标。

环境配置

推荐:Go 1.25+ 现代化配置

go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.cn,direct
go env -w GOSUMDB=sum.golang.google.cn

# 验证配置
go env | grep -E "(GO111MODULE|GOPROXY|GOSUMDB)"

macOS / Linux (Bash/Zsh)

export GO111MODULE=on
export GOPROXY=https://goproxy.cn,direct
export GOSUMDB=sum.golang.google.cn

echo 'export GO111MODULE=on' >> ~/.bashrc
echo 'export GOPROXY=https://goproxy.cn,direct' >> ~/.bashrc
echo 'export GOSUMDB=sum.golang.google.cn' >> ~/.bashrc
source ~/.bashrc

Windows (PowerShell)

$env:GO111MODULE="on"
$env:GOPROXY="https://goproxy.cn,direct"
$env:GOSUMDB="sum.golang.google.cn"

[Environment]::SetEnvironmentVariable("GO111MODULE", "on", "User")
[Environment]::SetEnvironmentVariable("GOPROXY", "https://goproxy.cn,direct", "User")
[Environment]::SetEnvironmentVariable("GOSUMDB", "sum.golang.google.cn", "User")

架构设计

项目结构

thinkgin/                    # 项目根目录
├── app/                     # 应用层
│   ├── index/               # 模块示例
│   │   ├── controller/      # 控制器层
│   │   ├── model/           # 数据模型层
│   │   └── view/            # 视图层
│   └── config.go            # 配置管理入口
├── config/                  # 配置文件目录
│   ├── app.yaml             # 应用主配置
│   ├── server.yaml          # 服务器配置
│   ├── database.yaml        # 数据库配置
│   ├── log.yaml             # 日志配置
│   └── prometheus.yaml      # 监控配置
├── extend/                  # 扩展组件
│   └── middleware/          # 中间件
├── route/                   # 路由定义
├── public/                  # 静态资源
├── runtime/                 # 运行时文件
└── main.go                  # 程序入口

请求流程

HTTP Request → Router → Middleware → Controller
                                          ↓
                                        Model ↔ Database
                                          ↓
                             View / JSON → HTTP Response

服务启动示例

config := app.GetConfig()
logger := app.GetLogger()

appInstance, err := framework.New(
    framework.WithConfig(config),
    framework.WithLogger(logger),
    framework.WithShutdownTimeout(10*time.Second),
)
if err != nil {
    logger.Fatalf("应用初始化失败: %v", err)
}

if err := appInstance.Run(ctx); err != nil {
    logger.Fatalf("服务器运行失败: %v", err)
}

配置管理

ThinkGin 采用模块化配置架构,把不同功能的配置拆分到独立的 YAML 文件中。

配置文件矩阵

配置文件 功能 重要性
app.yaml 应用元信息 + JWT 密钥 ⭐⭐⭐
server.yaml 监听地址 / 端口 / 超时 / 运行模式 ⭐⭐⭐
database.yaml MySQL / PostgreSQL / SQLite 连接 ⭐⭐⭐
cache.yaml Redis 多实例 ⭐⭐
session.yaml Session driver / Cookie 安全标志 ⭐⭐
middleware.yaml 全局中间件列表(session / jwt / cors / rate_limit…) ⭐⭐
log.yaml 日志级别 / 格式 / 轮转策略 ⭐⭐
prometheus.yaml 指标路径 + 分模块启停 ⭐⭐
trace.yaml OpenTelemetry 采样率 / 导出方式

使用示例

// 获取配置实例
config := app.GetConfig()

// 访问不同模块配置
appName := config.App.Name
serverPort := config.Server.HTTP.Port
dbConfig := config.Database.Default

app.yaml

app:
  name: "ThinkGin"
  version: "3.14.1"
  debug: true
  timezone: "Asia/Shanghai"
  swagger_enabled: true   # 启用 /swagger/ 端点
  jwt:
    secret: ""            # 留空,改用环境变量 THINKGIN_APP_JWT_SECRET 注入(v3.11.1+)
    expire: 7200     # 秒

server.yaml

server:
  http:
    host: "0.0.0.0"
    port: 8000
    read_timeout: 60
    write_timeout: 60
  mode: "debug"

其他配置模块

database.yaml

支持 MySQL / PostgreSQL / SQLite / Redis。

cache.yaml

支持 Redis、内存、文件缓存。

session.yaml

memory / redis 双 driver,含 Cookie 安全标志。

middleware.yaml

global 列表按名注册:session / jwt / cors / rate_limit…

trace.yaml

OpenTelemetry 采样率 / 导出方式 / tags。

lang (i18n)

YAML 翻译文件 + Accept-Language 检测 + T(c, key) 便捷函数。

Session

内置 memory / redis 两种 Store,Cookie 自动携带 HttpOnly / Secure / SameSite 安全标志。Session ID 采用 256-bit crypto/rand + base64url 编码。

启用

# config/middleware.yaml
middleware:
  global:
    - recovery
    - request_id
    - session        # 追加这一行即可

业务代码

import "thinkgin/app/session"

func Profile(c *gin.Context) {
    s := session.From(c)
    s.Set("user_id", 42)
    if err := s.Save(c.Request.Context()); err != nil {
        c.AbortWithError(500, err)
        return
    }
    c.JSON(200, gin.H{"ok": true})
}

func Logout(c *gin.Context) {
    _ = session.From(c).Destroy(c.Request.Context())
    session.ClearCookie(c)
}

driver 切换

driver 适用场景
memory 单实例 / 开发调试;默认值
redis 多副本部署;引用 database.yaml 中的 redis 连接
file / database 未实现

JWT 鉴权

HS256 签发与校验,严格拒绝 alg=none、非 HMAC 算法;密钥缺失时拒绝签发,避免"空密钥 token"。

配置

# config/app.yaml
app:
  jwt:
    secret: "your-secret-key"
    expire: 7200     # 秒

签发与校验

import "thinkgin/extend/middleware"

// 签发(ttl=0 使用 app.jwt.expire,仍为 0 兜底 2 小时)
token, err := middleware.JWTIssue(
    "42",
    map[string]any{"role": "admin"},
    0,
)

// 保护路由:middleware.yaml 加 "jwt",或在分组上 Use
api := r.Group("/api/v1", middleware.JWTAuth())
api.GET("/me", func(c *gin.Context) {
    claims := middleware.JWTFromContext(c)
    c.JSON(200, gin.H{
        "uid":  claims.Subject,
        "role": claims.Extra["role"],
    })
})

过期、签名非法、格式错误都统一走 APIError(401),响应体对外隐藏内部实现细节。

健康探针

两个探针的语义严格遵守 K8s 约定。

端点 语义 用途
GET /livez 仅验证 HTTP 处理管道存活 livenessProbe;失败即重启 Pod
GET /readyz 并发 Ping 所有已注册的 DB / Redis readinessProbe;失败时 k8s 摘掉 Pod 流量

/readyz 用 2s 硬超时,任一依赖不可达就返回 503 + 详细组件状态;没配置任何依赖时仍返回 200,兼容"纯 HTTP 应用"场景。

curl -i http://localhost:8000/readyz
# HTTP/1.1 200 OK
# {"status":"ok","components":{"database":{"ok":true,"elapsed":"2ms"}}}

日志系统

基于 slog(Go 1.21+ 标准库)的结构化日志系统,支持 JSON / Text 双格式,按时间或大小自动切割;v3.10 起默认 driver 已从 logrus 切换为 slog。

日志级别

级别 使用场景 示例
trace 详细跟踪 函数进入 / 退出
debug 调试信息 变量值、流程状态
info 一般信息 业务流程记录
warn 警告信息 潜在问题提醒
error 错误信息 异常处理
fatal 致命错误 程序无法继续
panic 恐慌级别 触发 panic

使用方法

import "thinkgin/extend/middleware"

// 记录信息日志
middleware.BusinessLogger("info", "用户登录成功", map[string]interface{}{
    "user_id": 123,
    "username": "john",
    "ip": "192.168.1.1",
})

// 记录错误日志
middleware.BusinessLogger("error", "数据库连接失败", map[string]interface{}{
    "error": err.Error(),
    "database": "mysql",
})

直接使用 logrus 实例

import "thinkgin/app"

logger := app.GetLogger()
logger.WithFields(logrus.Fields{
    "user_id": 123,
    "action": "update_profile",
}).Info("用户更新资料")

日志文件

  • 位置:runtime/log/
  • 命名:system.YYYYMMDD.log
  • 软链:system.log
  • 自动清理:超过设定天数的旧日志会被删除

开发环境配置

log:
  default:
    level: "debug"
    format: "text"
  file:
    max_age: 3
    rotation_time: 6

生产环境配置

log:
  default:
    level: "info"
    format: "json"
  file:
    max_age: 30
    rotation_time: 24

监控体系

ThinkGin 3.14.1 集成 Prometheus 监控服务,提供完整的应用性能监控和业务指标采集能力。

监控特性

  • HTTP 请求监控:自动记录请求数量、响应时间、请求体大小、响应体大小
  • 系统资源监控:CPU 使用率、内存使用量、进程信息
  • 业务指标:Counter / Histogram / Gauge 三种类型
  • 配置化:所有监控参数均可配置调整
  • 安全认证:支持基础认证保护监控端点
  • 自定义标签:支持自定义标签与命名空间

快速启用

# config/app.yaml
app:
  monitoring:
    prometheus_enabled: true   # 总开关
go run main.go
curl http://localhost:8000/metrics

k8s 探针

  • GET /livez
  • GET /readyz

prometheus.yaml 核心配置

prometheus:
  enabled: true            # 总开关;false 时不注册 /metrics
  path: "/metrics"
  service_name: "thinkgin"
  namespace: "app"
  metrics:
    http:
      enabled: true
      include_path: false  # 动态路径建议 false,避免 label 爆炸
    system:
      enabled: true
    business:
      enabled: true

业务埋点示例

import (
    "time"
    "thinkgin/extend/middleware"
    "github.com/gin-gonic/gin"
)

func YourHandler(c *gin.Context) {
    start := time.Now()

    if counter := middleware.BusinessCounter("api_calls", []string{"endpoint", "status"}); counter != nil {
        counter.WithLabelValues("/api/users", "success").Inc()
    }

    if histogram := middleware.BusinessHistogram("api_duration", []string{"endpoint"}, nil); histogram != nil {
        histogram.WithLabelValues("/api/users").Observe(time.Since(start).Seconds())
    }

    if gauge := middleware.BusinessGauge("online_users", []string{"type"}); gauge != nil {
        gauge.WithLabelValues("active").Set(123)
    }
}

常用 PromQL

# HTTP 请求总数
app_http_requests_total

# 平均响应时间
rate(app_http_request_duration_seconds_sum[5m]) / rate(app_http_request_duration_seconds_count[5m])

# 错误率(5xx)
rate(app_http_requests_total{status=~"5.."}[5m]) / rate(app_http_requests_total[5m])

# 内存使用量
app_system_memory_usage_bytes

自定义中间件注册表 v3.11.0 NEW

v3.10.x 之前,route.resolveMiddleware 是一个 18 分支的 hardcoded switch,业务想注入自定义中间件 唯一的方式是 fork 框架——这违反了开闭原则。v3.11.0 通过 route.RegisterMiddleware 让你在 main 早期注册任意命名工厂,之后即可在 config/middleware.yamlglobal / groups 列表里按名启用。

基本用法

package main

import (
    "github.com/gin-gonic/gin"
    "thinkgin/app"
    "thinkgin/framework"
    "thinkgin/route"
)

func main() {
    // 1. 在 framework.New 之前注册自定义中间件
    route.RegisterMiddleware("audit", func() gin.HandlerFunc {
        return func(c *gin.Context) {
            app.GetLogger().Infof("audit: %s %s", c.Request.Method, c.Request.URL.Path)
            c.Next()
        }
    })

    // 2. 也可以覆盖内置中间件(例如替换默认 access_log)
    route.RegisterMiddleware("access_log", func() gin.HandlerFunc {
        return myaccesslog.New()
    })

    a, _ := framework.New()
    _ = a.Run(nil)
}

然后在 config/middleware.yaml 的全局列表里按名启用:

middleware:
  global:
    - recovery
    - audit          # ← 自定义中间件
    - access_log     # ← 此处会用到上面注册的覆盖版本

API 参考

函数 说明
RegisterMiddleware(name, factory) 注册中间件工厂,重复注册同名将覆盖;空名或 nil 工厂被静默忽略
UnregisterMiddleware(name) 移除已注册项;主要用于测试隔离
LookupMiddleware(name) 查询并构造一个 gin.HandlerFunc 实例;未注册返回 nil
HasMiddleware(name) 仅做存在性判断,零分配,不构造 handler

设计要点

  • 工厂语义:注册的是 func() gin.HandlerFunc,每次 Use 时调用工厂得到独立实例,避免共享状态污染(例如令牌桶/熔断器内部计数器)。
  • 解析顺序:自定义注册表优先 → 未命中回落到内置 switch → 仍未命中静默忽略,100% 向后兼容 v3.10.x 配置。
  • 覆盖语义:注册同名中间件可覆盖内置实现(如自定义 logger 替代 access_log)。
  • 线程安全:基于 sync.Map,运行期读零开销;典型场景是 main 启动期一次性注册。
Tip · 注册必须在 route.InitRouter()(即 framework.New)之前完成,否则 YAML 配置解析时找不到自定义名称会被静默跳过。

ServiceContext 依赖注入

v3.3.0 引入 ServiceContext 替代全局单例,将 Config / Logger / DB / Cache 聚合到一个显式结构体中。

工作原理

// 在 Handler 中获取 ServiceContext
svc := app.SvcFromGin(c)
svc.Log.Infof("当前应用: %s", svc.Config.App.Name)

// ServiceContext 通过 SvcMiddleware 自动注入到 gin.Context
// 未注入时会从全局变量兜底,保持向后兼容

结构定义

type ServiceContext struct {
    Config *GlobalConfig
    Log    Logger
    DB     *gorm.DB      // 默认数据库连接
    Cache  *redis.Client  // 默认 Redis 连接
}
Tip · 保留了 app.GetConfig() / app.GetLogger() 全局函数,业务可逐步迁移,不会 break 现有代码。

安全中间件

v3.3.0 新增三大安全中间件,v3.6.1 进一步修复 CSRF 时序攻击,通过 middleware.global 列表按名启用。

CSRF 防护

# config/middleware.yaml
middleware:
  global:
    - recovery
    - csrf           # 双重提交 Cookie 方案
// 前端通过 Header 或表单字段提交 token
// Header: X-CSRF-Token: <token>
// 表单: _csrf_token=<token>

// 模板中使用:{{ .csrf_token }}

Gzip 压缩

middleware:
  global:
    - gzip           # 自动压缩响应

仅当客户端 Accept-Encoding 包含 gzip 时压缩,sync.Pool 复用 Writer,减少内存分配。

安全响应头

middleware:
  global:
    - secure_headers # 默认启用 X-Content-Type-Options/X-Frame-Options 等
  config:
    secure_headers:
      X-Frame-Options: "SAMEORIGIN"           # 覆盖默认值
      Content-Security-Policy: "default-src 'self'"  # 新增头
      X-Content-Type-Options: ""              # 空值 = 禁用

Swagger / OpenAPI

零 Go 依赖的 Swagger UI 端点,通过 CDN 加载 swagger-ui。

启用

# config/app.yaml
app:
  debug: true          # debug 模式自动启用
  # 或显式开启:
  swagger_enabled: true

端点

  • GET /swagger/ — Swagger UI 界面
  • GET /swagger/doc.yaml — OpenAPI YAML 规范
  • GET /swagger/doc.json — OpenAPI JSON 规范

规范文件

维护 docs/openapi.yaml 即可,修改后刷新 Swagger UI 即时生效。

数据库迁移

基于 GORM 的迁移管理器,迁移以 Go 函数形式注册,保持类型安全。

使用示例

import "thinkgin/app/database"

m := database.NewMigrator(db)
m.Register(
    database.Migration{
        Version: "001_create_users",
        Up: func(db *gorm.DB) error {
            return db.Exec("CREATE TABLE users (id INT PRIMARY KEY, name TEXT)").Error
        },
        Down: func(db *gorm.DB) error {
            return db.Exec("DROP TABLE IF EXISTS users").Error
        },
    },
)

// 执行迁移(幂等)
m.Migrate()

// 回退最后 1 个
m.Rollback(1)

// 查看状态
status, _ := m.Status()

配置热更新

使用 fsnotify 监听配置目录 YAML 文件变更,去抖 500ms 后自动重新加载。

使用方式

watcher, err := app.WatchConfig("config", func(cfg *app.GlobalConfig) {
    log.Infof("配置已重载: app.name=%s", cfg.App.Name)
    // 在此刷新运行时状态,如限流阈值、日志级别等
})
defer watcher.Close()
注意 · 热更新不会自动启用,需在 main.go 中显式调用 WatchConfig。Logger 等有状态组件建议不热换。

Redis 分布式限流

基于 Redis Lua 脚本的原子性令牌桶算法,适用于多实例部署统一限流。

配置

# config/middleware.yaml
middleware:
  global:
    - redis_rate_limit
  config:
    redis_rate_limit:
      requests_per_minute: 120
      redis_store: "default"    # 对应 cache.yaml 中的 store 名称
Tip · Redis 不可用时自动降级为 no-op(不限流),不会因限流系统故障导致全站不可访问。

国际化 i18n

YAML 翻译文件 + 自动语言检测 + 模板变量替换。

翻译文件

# lang/zh-CN.yaml
welcome: "欢迎, {{.Name}}!"
hello: "你好"

# lang/en.yaml
welcome: "Welcome, {{.Name}}!"
hello: "Hello"

使用方式

import "thinkgin/extend/i18n"

// 初始化
bundle := i18n.NewBundle("lang", "zh-CN")
bundle.LoadAll()

// 注册中间件
r.Use(i18n.Middleware(bundle))

// Handler 中翻译
func Hello(c *gin.Context) {
    msg := i18n.T(c, "welcome", map[string]string{"Name": "张三"})
    c.String(200, msg)
    // 输出:欢迎, 张三!(中文)或 Welcome, 张三!(英文)
}

语言检测优先级

  1. ?lang=en 查询参数(最高优先级)
  2. Accept-Language 请求头
  3. fallback 默认语言

WebSocket

基于 gorilla/websocket 封装,一行代码将 HTTP 请求升级为 WebSocket 连接。

Echo 示例

import "thinkgin/extend/websocket"

r.GET("/ws", websocket.Handler(func(conn *websocket.Conn) {
    defer conn.Close()
    for {
        mt, msg, err := conn.Conn.ReadMessage()
        if err != nil { break }
        conn.WriteSafeMessage(mt, msg) // echo back
    }
}))

Hub 广播模型

适用于聊天室、实时通知等场景,自动管理连接生命周期。

hub := websocket.NewHub()

r.GET("/ws", websocket.Handler(func(conn *websocket.Conn) {
    hub.Register(conn)
    defer hub.Unregister(conn)

    for {
        _, msg, err := conn.Conn.ReadMessage()
        if err != nil { break }
        // 广播给所有连接
        hub.Broadcast(ws.TextMessage, msg)
    }
}))

// 也支持广播 JSON
hub.BroadcastJSON(map[string]string{"event": "new_user", "name": "张三"})

自定义 Upgrader

strictUpgrader := gorilla.Upgrader{
    CheckOrigin: func(r *http.Request) bool {
        return r.Header.Get("Origin") == "https://yourdomain.com"
    },
}
r.GET("/ws", websocket.HandlerWithUpgrader(handler, strictUpgrader))
注意 · 默认 CheckOrigin 放通所有 origin,生产环境请务必替换为严格的白名单检查。

线程安全

Conn.WriteJSONConn.WriteSafeMessage 内部加锁,支持多 goroutine 并发写同一连接。读操作仍需单 goroutine 串行。

熔断器中间件

基于滑动窗口的熔断器(Circuit Breaker),v3.7.2 起支持按路由粒度隔离,避免单个慢接口拖垮全局。

状态机

三态自动切换:Closed(正常)→ Open(拒绝,返回 503)→ HalfOpen(试探)→ Closed(恢复)。

使用方式

# config/middleware.yaml
middleware:
  global:
    - recovery
    - circuit_breaker

自定义配置

cfg := middleware.CircuitBreakerConfig{
    WindowSize:            100,  // 滑动窗口大小
    ErrorThresholdPercent: 50,   // 错误率阈值 (%)
    CooldownDuration:      10 * time.Second,
    HalfOpenMaxRequests:   5,    // 半开放试探请求数
    IsServerError: func(code int) bool {
        return code >= 500
    },
}
r.Use(middleware.CircuitBreakerWithConfig(cfg))
Tip · 默认配置:窗口 100 请求、50% 错误率触发、10s 冷却、5 次试探。通过 middleware.CircuitBreaker() 直接使用。

超时控制中间件

请求超时控制,为每个请求设置 context.WithTimeout,超时返回 504 Gateway Timeout

使用方式

# config/middleware.yaml
middleware:
  global:
    - recovery
    - timeout

自定义超时

// 默认 30s
r.Use(middleware.Timeout())

// 自定义 5s
r.Use(middleware.TimeoutWithDuration(5 * time.Second))

Handler 配合

func handler(c *gin.Context) {
    ctx := c.Request.Context()
    select {
    case result := <-doWork(ctx):
        c.JSON(200, result)
    case <-ctx.Done():
        return // 超时,中间件自动返回 504
    }
}
注意 · 下游 handler 应使用 c.Request.Context() 感知超时信号,以便及时终止数据库查询或外部 API 调用。

请求体大小限制

请求体大小限制中间件,双重保护:Content-Length 预检 + MaxBytesReader 流式保护。

使用方式

# config/middleware.yaml
middleware:
  global:
    - recovery
    - body_limit       # 默认 10MB

自定义大小

// 按字节数
r.Use(middleware.BodyLimitWithSize(5 << 20)) // 5MB

// 按字符串解析 (支持 B/KB/MB/GB)
h, _ := middleware.BodyLimitFromString("512KB")
r.Use(h)

解析工具

bytes, err := middleware.ParseBodyLimit("10MB")   // 10485760
bytes, err := middleware.ParseBodyLimit("0.5GB")  // 536870912
bytes, err := middleware.ParseBodyLimit("512KB")  // 524288
Tip · 超限请求返回 413 Payload Too Large,并在响应体中提示当前限制值。

脚手架 CLI

用一条命令生成新业务模块的 MVC 骨架,取代手工创建 controller / model / view 的重复劳动。

# 生成 app/user/ 下的 controller.go / model.go / view/ 三件套
go run ./cmd/scaffold new module user

命名规则:模块名必须以小写字母开头,只允许 a-z / 0-9 / _。生成后请在 route/ 中注册路由。

数据库迁移

框架不内置迁移工具。AutoMigrate 只适合开发期试错,生产环境推荐 pressly/goose 做版本化迁移。

go install github.com/pressly/goose/v3/cmd/goose@latest

# 目录约定:db/migrations/00001_create_users_table.sql
goose -dir db/migrations mysql "thinkgin:thinkgin@tcp(127.0.0.1:3306)/thinkgin?parseTime=true" up

详细指引见仓库中的 docs/database-migration.md。替代方案:golang-migrate/migratesqlc 等。

开发指南

路由规范

// route/router.go
func SetupRoutes(r *gin.Engine) {
    api := r.Group("/api/v1")
    {
        api.GET("/users", controller.GetUsers)
        api.POST("/users", controller.CreateUser)
        api.PUT("/users/:id", controller.UpdateUser)
        api.DELETE("/users/:id", controller.DeleteUser)
    }
}

响应格式

{
  "code": 200,
  "data": {
    "Time": "2023-02-01T10:11:41.761Z",
    "data": "Hello ThinkGin!",
    "name": ""
  },
  "msg": "ok"
}

开发最佳实践

  • 目录规范:按模块组织代码结构
  • 配置驱动:环境差异全部交给配置文件
  • 日志规范:使用结构化字段而不是拼接字符串
  • 测试覆盖:单元测试 + 集成测试
  • 监控埋点:在关键业务流程写入指标

部署指南

Docker 部署(推荐)

FROM golang:1.25-alpine AS builder
WORKDIR /app
COPY . .
RUN go mod tidy && go build -o thinkgin

FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/thinkgin .
COPY --from=builder /app/config ./config
CMD ["./thinkgin"]
docker build -t thinkgin:v3.1.0 .
docker run -p 8000:8000 thinkgin:v3.1.0

跨平台编译

# Linux
GOOS=linux GOARCH=amd64 go build -o thinkgin-linux

# Windows
GOOS=windows GOARCH=amd64 go build -o thinkgin.exe

# macOS
GOOS=darwin GOARCH=amd64 go build -o thinkgin-macos

Linux 后台运行

nohup ./thinkgin 1>info.log 2>&1 &

# 使用 systemd 管理
sudo systemctl enable thinkgin
sudo systemctl start thinkgin
Note · 不要把编译后的二进制提交到 Git;不同平台通过编译命令按需生成即可。

常见问题

Go 版本过低

# 错误: go: thinkgin requires go >= 1.18
# macOS
brew install go
# 其他平台下载最新版: https://golang.org/dl/

端口占用

# Linux / macOS
lsof -ti:8000 | xargs kill -9

# Windows
netstat -ano | findstr :8000
taskkill /PID <PID> /F

网络连接问题

go env -w GOPROXY=https://goproxy.cn,direct

模块依赖问题

go clean -modcache
go mod download
go mod tidy

配置文件问题

ls -la config/

pip install yamllint
yamllint config/app.yaml

内存不足

# 调整 Go 垃圾回收
export GOGC=50
export GOMEMLIMIT=512MB  # Go 1.19+

故障排查命令

go version && go env
curl -I https://goproxy.cn
netstat -tlnp | grep :8000
tail -f runtime/log/system.log
Tip · 遇到问题时,请按顺序检查 runtime/log/ 日志 → config/ YAML 语法 → go env 环境,然后再提交 issue。