每日一Go-53、Go微服务--限流与降级

0 阅读5分钟

在微服务架构中,有一句话非常残酷,但极其真实:系统不是被“慢”拖死的,而是被“瞬时洪峰”打死的。

昨天我们讲了 超时 + 熔断,那是“服务已经出问题时的自我保护”;而今天的 限流与降级,是更靠前的一道防线——在问题发生之前,先把系统保住。

🫱🫱🫱文末有源码🫲🫲🫲

一、什么是限流?什么是降级?

已关注

关注

重播 分享 赞

关闭

观看更多

更多

退出全屏

切换到竖屏全屏**退出全屏

Codee君已关注

分享视频

,时长00:04

0/0

00:00/00:04

切换到横屏模式

继续播放

进度条,百分之0

播放

00:00

/

00:04

00:04

倍速

全屏

倍速播放中

0.5倍 0.75倍 1.0倍 1.5倍 2.0倍

超清 流畅

您的浏览器不支持 video 标签

继续观看

每日一Go-53、Go微服务--限流与降级

观看更多

转载

,

每日一Go-53、Go微服务--限流与降级

Codee君已关注

分享点赞在看

已同步到看一看写下你的评论

视频详情

1. 限流:限流的本质就一句话--系统吃得下多少请求,就只给多少。超过处理能力的请求直接拒绝;防止CPU、连接池、下游服务被瞬间打爆。限流通常发生在:API网关、服务入口(Gin中间件)、核心接口(登录、下单、支付)

2. 降级:降级不是失败,而是主动放弃“不重要的部分”。

二、Gin中实现限流--令牌桶模型

1. 核心思路

桶里有n个令牌,每秒补充m个,每个请求消耗一个,没令牌就直接拒绝。

2. 限流器实现

package limiter
import (
    "sync"
    "time"
)
// TokenBucket 实现了令牌桶限流算法
type TokenBucket struct {
    capacity int        // 令牌桶容量
    tokens   int        // 当前令牌数量
    rate     int        // 每秒生成令牌速率
    lock     sync.Mutex // 互斥锁,保证线程安全
}
// NewTokenBucket 创建一个新的令牌桶限流器
// capacity: 令牌桶容量,表示最多可以存储多少个令牌
// rate: 每秒生成的令牌数量
func NewTokenBucket(capacity, rate int) *TokenBucket {
    tb := &TokenBucket{
        capacity: capacity, // 设置令牌桶容量
        tokens:   capacity, // 初始时令牌桶是满的
        rate:     rate,     // 设置每秒生成令牌的速率
    }
    // 启动令牌生成协程
    go tb.refill()
    return tb
}
// refill 定时生成令牌的方法
func (tb *TokenBucket) refill() {
    // 创建一个每秒触发一次的定时器
    ticker := time.NewTicker(time.Second)
    // 无限循环,每秒向令牌桶中添加令牌
    for range ticker.C {
        tb.lock.Lock()
        // 向令牌桶中添加rate个令牌
        tb.tokens += tb.rate
        // 确保令牌数量不超过桶容量
        if tb.tokens > tb.capacity {
            tb.tokens = tb.capacity
        }
        tb.lock.Unlock()
    }
}
// Allow 判断是否允许请求通过
// 返回true表示允许,false表示拒绝
func (tb *TokenBucket) Allow() bool {
    tb.lock.Lock()
    defer tb.lock.Unlock()
    // 如果有可用令牌,消耗一个令牌并返回true
    if tb.tokens > 0 {
        tb.tokens--
        return true
    }
    // 没有可用令牌,返回false
    return false
}

3. Gin中间件接入限流

package middleware
import (
    "day53/limiter"
    "net/http"
    "github.com/gin-gonic/gin"
)
// RateLimit 创建一个基于令牌桶的Gin限流中间件
// tb: 令牌桶限流器实例
func RateLimit(tb *limiter.TokenBucket) gin.HandlerFunc {
    return func(c *gin.Context) {
        // 检查是否允许请求通过限流器
        if !tb.Allow() {
            // 如果请求被限流,返回429 Too Many Requests状态码
            c.JSON(http.StatusTooManyRequests, gin.H{
                "error""too many requests",
            })
            // 终止请求处理链
            c.Abort()
            return
        }
        // 如果允许请求通过,继续处理下一个中间件
        c.Next()
    }
}

三、Gin中实现服务降级

降级的关键不是考虑代码怎么写,而是要承认一件事:有些功能,在系统压力下,不值得活。

1. 降级策略

  • 系统繁忙->返回简化数据

  • 系统正常->返回完整数据

2. 示例接口

package main
import (
    "fmt"
    "math/rand/v2"
    "net/http"
    "time"
    "day53/limiter"
    "day53/middleware"
    "github.com/gin-gonic/gin"
)
// main 函数是程序的入口点
func main() {
    // 打印欢迎信息
    fmt.Println("Hello, Codee君!\nWelcome to golang_per_day")
    r := gin.Default()
    // 创建令牌桶限流器:容量为10,每秒生成5个令牌
    tb := limiter.NewTokenBucket(105)
    // 将限流中间件应用到所有路由
    r.Use(middleware.RateLimit(tb))
    r.GET("/profile/:id"func(c *gin.Context) {
        id := c.Param("id")
        // 模拟下游服务返回 503 触发降级
        if rand.Float32() < 0.4 {
            // 触发降级
            c.JSON(http.StatusOK, gin.H{
                "user_id": id,
                "name":    "anonymous",
                "remark":  "degraded response",
            })
            return
        }
        // 正常完整响应
        time.Sleep(100 * time.Millisecond)
        c.JSON(http.StatusOK, gin.H{
            "user_id": id,
            "name":    "Tom",
            "age":     28,
            "email":   "tom@example.com",
        })
    })
    r.Run(":8080")
}

四、限流+降级=微服务的最后防线

在真实系统中,顺序通常是:

限流->超时->熔断->降级

  • 限流:挡住洪水

  • 超时:不被慢服务拖死

  • 熔断:隔离失败

  • 降级:核心功能活下来

五、人生比喻

1. 限流,是你对人生的清醒认知;你每天的精力是有限的,不是什么请求都要答应,不是什么事情都值得立刻处理。学会拒绝,是成熟的开始。

2. 降级,是你对现实的妥协智慧;人生不顺时,不要求完美输出,先保证还能继续走。系统可以返回简化数据,人也可以暂时降低标准。

3. 高可用的人生:真正厉害的人,不是永远满负荷运行,而是在压力来临时,主动限流;在扛不住时,果断降级;但核心价值,永远不崩。

六、一句话总结

限流,是对能力边界的尊重;

降级,是对现实压力的妥协;

而能长期稳定运行的系统和人生,都懂得什么时候该“少做一点”。

*源码地址*

1、公众号“Codee君”回复“每日一Go”获取源码

2、pan.baidu.com/s/1B6pgLWfS… 


如果您喜欢这篇文章,请您(点赞、分享、亮爱心),万分感谢!