【微服务网关——Go令牌桶限流】

在这里插入图片描述

1. time/rate限速器使用

  • 令牌桶限流算法
  • rate.NewLimiter(limit,burst)产生一个新的限速器
    • limit表示每秒产生token数、burst表示最多存token数
  • Allow判断当前是否可以取到token
  • Wait阻塞等待直到取到token
  • Reverse返回等待时间(预估的等待时间),再去取token
package mainimport ("context""golang.org/x/time/rate""log""testing""time"
)func Test_RateLimiter(t *testing.T) {l := rate.NewLimiter(1, 5)log.Println(l.Limit(), l.Burst())for i := 0; i < 10; i++ {//阻塞等待直到,取到一个tokenlog.Println("before Wait")c, _ := context.WithTimeout(context.Background(), time.Second*2)if err := l.Wait(c); err != nil {log.Println("limiter wait err:" + err.Error())}log.Println("after Wait")//返回需要等待多久才有新的token,这样就可以等待指定时间执行任务r := l.Reserve()log.Println("reserve Delay:", r.Delay())//判断当前是否可以取到tokena := l.Allow()log.Println("Allow:", a)log.Println("======================")}
}

在这里插入图片描述

2. time/rate源码原理

  • 计算上次请求和当前请求时间差
  • 计算时间差内生成的token数+旧token数
  • 如果token为负,则计算等待时间
  • token为正,则请求后token-1
type Limit float64type Limiter struct {limit Limit//每秒产生的token数burst int//桶的总大小mu     sync.Mutex//锁tokens float64//token总数last time.Time//上一次更新token的时间lastEvent time.Time//最后一次限速的时间
}

Allow、Reverse、Wait三个方法底层调用的都是func (lim *Limiter) reserveN(now time.Time, n int, maxFutureReserve time.Duration) Reservation

// reserveN 是 AllowN、ReserveN 和 WaitN 的辅助方法。
// maxFutureReserve 指定了允许的最大预订等待时间。
// reserveN 返回 Reservation(而不是 *Reservation),以避免在 AllowN 和 WaitN 中进行分配。
func (lim *Limiter) reserveN(now time.Time, n int, maxFutureReserve time.Duration) Reservation {// 加锁,保护临界区lim.mu.Lock()// 如果每秒产生的token数为无限,则无需预订直接返回if lim.limit == Inf {lim.mu.Unlock()return Reservation{ok:        true,        // 预订成功lim:       lim,         // 当前限流器tokens:    n,           // 预订的令牌数timeToAct: now,         // 立即生效}}// 更新当前时间、上次时间和现在可用令牌数now, last, tokens := lim.advance(now)// 计算请求n个tokens后的剩余令牌数tokens -= float64(n)// 计算等待时长var waitDuration time.Durationif tokens < 0 {// 如果令牌不够,需要等待的时间waitDuration = lim.limit.durationFromTokens(-tokens)}// 判断预订是否成功,请求的n是否小于等于桶的容量,且等待时间是否小于用户给的最大实践ok := n <= lim.burst && waitDuration <= maxFutureReserve// 准备预订结果r := Reservation{ok:    ok,            // 预订是否成功lim:   lim,           // 当前限流器limit: lim.limit,     // 当前限流器的限制}if ok {r.tokens = n               // 成功预订的令牌数r.timeToAct = now.Add(waitDuration) // 生效时间}// 更新限流器状态if ok {lim.last = now              // 更新上次预订时间lim.tokens = tokens         // 更新剩余令牌数lim.lastEvent = r.timeToAct // 更新上次事件时间} else {lim.last = last             // 未成功则恢复上次时间}// 解锁lim.mu.Unlock()return r  // 返回预订结果
}// advance 计算并返回基于时间推移的 lim 的更新状态。
// lim 自身的状态不会被改变。
func (lim *Limiter) advance(now time.Time) (newNow time.Time, newLast time.Time, newTokens float64) {last := lim.lastif now.Before(last) {// 如果 now 比 last 还早,则使用 now 作为 lastlast = now}// 避免 last 非常久远时导致 delta 溢出。// 计算多久后这个桶会自动填满maxElapsed := lim.limit.durationFromTokens(float64(lim.burst) - lim.tokens)elapsed := now.Sub(last)if elapsed > maxElapsed {// 如果实际时间间隔超过最大允许间隔,调整为最大间隔,避免由于非常大的 elapsed 造成溢出或不合理的计算。elapsed = maxElapsed}// 计算由于时间推移增加的令牌数delta := lim.limit.tokensFromDuration(elapsed)tokens := lim.tokens + deltaif burst := float64(lim.burst); tokens > burst {// 如果计算得到的令牌数超过了 burst,则限制为 bursttokens = burst}// 返回更新后的时间 now, 上次时间 last 以及新的令牌数 tokensreturn now, last, tokens
}// tokensFromDuration 是一个单位转换函数,
// 用于将时间段转换为在该时间段内以每秒 limit 个令牌的速率
// 可积累的令牌数。
func (limit Limit) tokensFromDuration(d time.Duration) float64 {// 自行分离整数部分和小数部分,以尽量减少舍入误差。// 参考 golang.org/issues/34861。sec := float64(d / time.Second) * float64(limit) // 计算整秒内的令牌数nsec := float64(d % time.Second) * float64(limit) // 计算剩余纳秒内的令牌数return sec + nsec / 1e9 // 返回整秒和纳秒对应的令牌数之和
}

func (lim *Limiter) reserveN(now time.Time, n int, maxFutureReserve time.Duration) Reservation

  • AllowN(now time.Time, n int) bool
    • lim.reserveN(now, n, 0).ok
      • now表示现在
      • n表示请求n个token
      • 0表示等待时间
  • ReserveN
    • lim.reserveN(now, n, InfDuration)
      • now表示现在
      • n表示请求n个token
      • InfDuration表示无限等待
  • WaitN
// WaitN 阻塞直到 lim 允许 n 个事件发生。
// 如果 n 超过了 Limiter 的 burst 大小,Context 被取消,
// 或者预期的等待时间超过了 Context 的截止时间,它会返回一个错误。
// 如果速率限制是无限的(Inf),则忽略 burst 限制。
func (lim *Limiter) WaitN(ctx context.Context, n int) (err error) {// 加锁以安全地获取限流器的 burst 和 limit 值lim.mu.Lock()burst := lim.burstlimit := lim.limitlim.mu.Unlock()// 如果 n 超过了 burst 且 limit 不是 Inf,则返回错误if n > burst && limit != Inf {return fmt.Errorf("rate: Wait(n=%d) exceeds limiter's burst %d", n, lim.burst)}// 检查 Context 是否已取消select {case <-ctx.Done():return ctx.Err()default:}// 查看ctx是否设定了deadline,确定最大等待时间now := time.Now()waitLimit := InfDurationif deadline, ok := ctx.Deadline(); ok {// 计算距离截止时间的剩余时间waitLimit = deadline.Sub(now)}// 进行预订r := lim.reserveN(now, n, waitLimit)if !r.ok {// 如果预订失败且等待时间超过 Context 截止时间,返回错误return fmt.Errorf("rate: Wait(n=%d) would exceed context deadline", n)}// 计算需要等待的时间delay := r.DelayFrom(now)if delay == 0 {// 如果不需要等待,直接返回return nil}// 启动定时器进行等待t := time.NewTimer(delay)defer t.Stop()select {case <-t.C:// 拿到了令牌return nilcase <-ctx.Done():// 在等待时 Context 被取消,取消预订,允许其他事件提前进行r.Cancel()return ctx.Err()}
}

3. 小结

令牌桶算法广泛应用于控制 API 请求速率、限制资源访问频率、管理任务调度等场景。通过合理设置 limit 和 burst,可以有效平衡系统负载和服务质量。该算法并不会实时去维护令牌桶中的token的数量,而是通过last和lastEvent来巧妙的计算出该段时间内容桶内令牌的状态,同时通过锁来维护了对于令牌桶的访问一致性问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/35454.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

浅谈业务开发与非业务开发

浅谈业务开发与非业务开发 软件开发业务开发非业务开发工作量的区别 软件开发 在谈及业务开发与非业务开发之前&#xff0c;首先他们都是软件开发&#xff0c;那么软件开发的流程是怎样的呢&#xff1f;我们先来了解一下软件开发的流程。通常情况下软件开发的流程是这样的 在…

excel实现下拉筛选(超简单)

excel实现下拉筛选 引言1、需求&#xff1a;预警状态下的列 实现下拉筛选2、实现2.1、数据验证2.2、下拉筛选内容2.3、去掉预警状态单元格的下拉筛选 引言 通常&#xff0c;我们会单独新建一张sheet表 专门存每个列的下拉内容。下面我将专门建立一张名为代码表的sheet表来存放…

光模块市场受益于AI热潮同比增长45%,行业前景看好

近日&#xff0c;市场研究机构YOLE Group在最新的市场报告中指出&#xff0c;AI驱动的光模块市场将出现同比45%的增长。预计至2024年&#xff0c;数据通信领域的人工智能光收发器市场将实现高达45%的同比增长&#xff0c;展现出了强大的市场活力和广阔的发展前景。 光收发器市…

回购注销高管减持,东软集团的“大手笔”有意义吗?

文&#xff1a;互联网江湖 作者&#xff1a;刘致呈 作为老牌软件巨头&#xff0c;东软集团这两年的业绩着实有些不够看。 看财报数据&#xff0c;22年东软集团营收94.66亿&#xff0c;净亏损3.47亿&#xff0c;扣非净利利润-5.30亿。23年&#xff0c;集团营收105.44亿&#x…

煤安防爆手机为什么能在煤矿井下使用

煤安防爆手机之所以能在煤矿井下使用&#xff0c;是因为它们经过特殊设计&#xff0c;符合严格的防爆安全标准&#xff0c;能够防止电火花引发爆炸&#xff0c;同时具备防尘防水、抗冲击等特性&#xff0c;确保在恶劣的煤矿环境中稳定可靠地运行&#xff0c;为工作人员提供安全…

前端 CSS 经典:图层放大的 hover 效果

效果 思路 设置 3 层元素&#xff0c;最上层元素使用 clip-path 裁剪成圆&#xff0c;hover 改变圆大小&#xff0c;添加过渡效果。 实现代码 <!DOCTYPE html> <html lang"en"><head><meta charset"utf-8" /><meta http-eq…

RSA非对称加密-openssl命令及C语言实现

RSA加密算法是一种非对称加密算法。在公开密钥加密和电子商业中RSA被广泛使用。本文介绍如何使用openssl命令和C代码实现基础的RSA加/解密和签名/验签功能。 一、openssl命令实现RSA加解密 1、生成私钥和公钥 生成私钥 openssl genrsa -out private.key 2048 #…

前端 CSS 经典:mix-blend-mode 属性

前言&#xff1a;这是一个混合属性&#xff0c;作用是将两个颜色混合生成一个新颜色。可以将视频和文字相融合&#xff0c;产生动态文字效果。 效果 实现代码 <!DOCTYPE html> <html lang"en"><head><meta charset"utf-8" />&l…

cuav系列飞控关于MAIN-IO 的输出数量控制

在无人机设置中&#xff0c;经常涉及到使用AUX辅助通道来控制附属设备&#xff0c;或者直接把主控输出通道放到AUX。但是CUAV系列飞控按照常规设置并不能正常启用AUX输出&#xff0c;通过查看飞控源码找到 原因 一些机型例如24001使用了12个电机&#xff0c;常规主通道只有8个&…

【神经网络】CNN网络:深入理解卷积神经网络

&#x1f388;个人主页&#xff1a;豌豆射手^ &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共同学习、交流进步&#xff01; CNN网络&#xff1a;深入理解…

常用的企业级快速传输大文件平台

在当今企业运营中&#xff0c;数据管理成了一项不可或缺的任务。企业每日需处理庞大的数据量&#xff0c;这包括高清视频、大量数据集和复杂的设计图纸等大型文件。然而&#xff0c;传统的文件传输手段&#xff0c;比如通过电子邮件发送附件或使用FTP服务&#xff0c;已经难以满…

完成一个有趣的Web期末大作业(html、css、javascript、MySQL、Node.js)

题目&#xff1a;学校老师的要求很开放&#xff0c;要自己做一个感兴趣的网页&#xff0c;要求使用基础的html、css和javascript&#xff0c;后端要使用数据库。 网上都是各种管理系统&#xff0c;看多了觉得没啥意思&#xff0c;要做一个自己感兴趣的网站。近几年沉迷犬夜叉这…

“论大数据处理架构及其应用”写作框架,软考高级,系统架构设计师

论文真题 大数据处理架构是专门用于处理和分析巨量复杂数据集的软件架构。它通常包括数据收集、存储、处理、分析和可视化等多个层面&#xff0c;旨在从海量、多样化的数据中提取有价值的信息。Lambda架构是大数据平台里最成熟、最稳定的架构&#xff0c;它是一种将批处理和流…

AI图书下载:《ChatGPT百万富翁-最快赚钱之道》

《ChatGPT百万富翁-最快赚钱之道》&#xff08;ChatGPT Millionaire. The Fastest Way To Make Real Money&#xff09;是一本集合了五本书内容的作品&#xff0c;由Harper Hanz编写&#xff0c;旨在探讨如何利用ChatGPT这一强大的自然语言处理系统创造被动收入。 以下是该书各…

Mysql索引和事务

一、索引是做什么的? 很多时候&#xff0c;当你的应用程序进行SQL查询速度很慢时&#xff0c;应该想想是否可以建索引。 大多数MySQL索引(PRIMARY KEY、UNIQUE、INDEX和FULLTEXT)在B树中存储。只是空间列类型的索引使用R-树&#xff0c;并且MEMORY表还支持hash索引。 索引是…

Ubuntu-22.04 安装禅道

&#x1f680;write in front&#x1f680; &#x1f50e;大家好&#xff0c;我是黄桃罐头&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流 &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd;​…

螺栓的拧紧扭矩计算

对于采用控制扭矩方式拧紧的螺栓连接而言&#xff0c;螺栓扭矩是一个非常重要的参数&#xff0c;扭矩的大小决定了螺栓预紧力的大小&#xff0c;而螺栓预紧力又是预紧型螺栓连接的灵魂。前文讨论了螺栓扭矩的校验&#xff0c;即如何验证螺栓扭矩是否满足设计要求&#xff0c;与…

python-登录界面-demo

文章目录 前言python-登录界面-demo 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff0c;写作不易啊^ _ ^。   而且听说点赞的人每天的运气都不会太差&#xff0c;实在白嫖的话&#xff0c;那欢迎常来啊!!! python-…

Flask之表单

前言&#xff1a;本博客仅作记录学习使用&#xff0c;部分图片出自网络&#xff0c;如有侵犯您的权益&#xff0c;请联系删除 目录 一、HTML表单 二、使用Flask-WTF处理表单 2.1、定义WTForms表单类 2.2、输出HTML代码 2.3、在模板中渲染表单 三、处理表单数据 3.1、提…

geojson文件默认已有的style会导致webGL渲染错误处理办法

geojson文件默认已有的style会导致webGL渲染错误处理办法 相关链接&#xff1a; 功能示例(Vue版) | Mars3D三维可视化平台 | 火星科技 代码&#xff1a; export function showDraw(isFlyTo) {removeLayer()graphicLayer new mars3d.layer.GeoJsonLayer({data: {type: &quo…