← 返回博客
go2026-09-12 22:07:462 分钟 · 548 0

Gin 灰度发布与流量调度

用 gin 中间件按用户、按比例把流量分到新旧版本,讲清蓝绿、金丝雀两种灰度模式,以及配套的健康检查和自动回滚。

新版本上线直接全量,一旦有 bug 全员受影响。灰度先放一小撮流量验证,没问题再扩。这一篇在 Gin 里做流量分流,配合部署侧的蓝绿和金丝雀。

1. 两种灰度模式

蓝绿:两套完整环境,流量整切,出问题整切回。金丝雀:新旧并存,按比例放小流量(1%→10%→100%),渐进验证。

2. 按比例分流中间件

按 uid 哈希稳定分流,同一用户始终落同一版本:

func Canary(percent int, handlerNew gin.HandlerFunc) gin.HandlerFunc {
    return func(c *gin.Context) {
        uid := c.GetString("uid")
        if uid == "" {
            c.Next()
            return
        }
        h := fnvHash(uid) % 100
        if h < percent {
            handlerNew(c)
            return
        }
        c.Next()
    }
}

哈希保证用户不忽新忽旧,体验一致。

3. 按规则精准灰度

白名单、内部账号、特定地域先开:

func CanaryRule(c *gin.Context) bool {
    if inAllowlist(c.GetString("uid")) {
        return true
    }
    if c.GetHeader("X-Canary") == "1" {
        return true
    }
    return false
}

测试同学带 X-Canary: 1 头就能稳定命中新版本,不用等比例。

4. 多版本怎么共存

同一进程内用函数分流(上面写法)。跨服务、跨部署单元,靠网关或 service mesh(如 Istio)按权重路由,Gin 只报自己的版本号:

c.Header("X-Version", "v2")

上游网关读版本头做统计,确认新版本错误率正常再加大权重。

5. 健康检查决定流量

灰度期间新版本必须暴露 /healthz(第 8 篇讲过)。网关探活失败自动摘流量,新版本启动异常不会接客:

r.GET("/healthz", func(c *gin.Context) {
    if db.Ping() != nil {
        c.JSON(503, gin.H{"status": "db down"})
        return
    }
    c.JSON(200, gin.H{"status": "ok"})
})

6. 自动回滚信号

灰度放大前看三个数:错误率、P99、QPS 对比旧版本。错误率超基线就回滚。回滚在部署侧做(把权重调 0 或切回蓝绿旧环境),Gin 这层只保证自己健康时可被摘流。

7. 上线清单

分流用稳定哈希,用户不跳版本。白名单和手动标先于比例,方便测试。新版本必须带健康检查,否则网关不敢放量。灰度放大前对比新旧错误率/P99,异常立即回滚。蓝绿模式保留旧环境一段时间再下线,方便整切回。

下一篇讲异步任务,把耗时操作从请求链路里摘出来。

相关推荐

本文为原创文章,采用CC BY-NC-SA 4.0协议授权,转载请保留署名与原文链接。原文链接:https://www.wxbuluo.com/article/196