go-zero 如何扛住流量冲击(二)
本篇文章承接上一篇go-zero 如何扛住流量冲击(一)。
上一篇介绍的是 go-zero
中滑动窗口限流,本篇介绍另外一个 tokenlimit
,令牌桶限流。
使用
const ( burst = 100 rate = 100 seconds = 5 ) store := redis.NewRedis("localhost:6379", "node", "") fmt.Println(store.Ping()) // New tokenLimiter limiter := limit.NewTokenLimiter(rate, burst, store, "rate-test") timer := time.NewTimer(time.Second * seconds) quit := make(chan struct{}) defer timer.Stop() go func() { <-timer.C close(quit) }() var allowed, denied int32 var wait sync.WaitGroup for i := 0; i < runtime.NumCPU(); i++ { wait.Add(1) go func() { for { select { case <-quit: wait.Done() return default: if limiter.Allow() { atomic.AddInt32(&allowed, 1) } else { atomic.AddInt32(&denied, 1) } } } }() } wait.Wait() fmt.Printf("allowed: %d, denied: %d, qps: %d\n", allowed, denied, (allowed+denied)/seconds)
tokenlimit
从整体上令牌桶生产token逻辑如下:
- 用户配置的平均发送速率为r,则每隔1/r秒一个令牌被加入到桶中;
- 假设桶中最多可以存放b个令牌。如果令牌到达时令牌桶已经满了,那么这个令牌会被丢弃;
- 当流量以速率v进入,从桶中以速率v取令牌,拿到令牌的流量通过,拿不到令牌流量不通过,执行熔断逻辑;
go-zero
在两类限流器下都采取 lua script
的方式,依赖redis可以做到分布式限流,lua script
同时可以做到对 token 生产读取操作的原子性。
下面来看看 lua script
控制的几个关键属性:
argument | mean |
---|---|
ARGV[1] | rate 「每秒生成几个令牌」 |
ARGV[2] | burst 「令牌桶最大值」 |
ARGV[3] | now_time「当前时间戳」 |
ARGV[4] | get token nums 「开发者需要获取的token数」 |
KEYS[1] | 表示资源的tokenkey |
KEYS[2] | 表示刷新时间的key |
-- 返回是否可以活获得预期的token local rate = tonumber(ARGV[1]) local capacity = tonumber(ARGV[2]) local now = tonumber(ARGV[3]) local requested = tonumber(ARGV[4]) -- fill_time:需要填满 token_bucket 需要多久 local fill_time = capacity/rate -- 将填充时间向下取整 local ttl = math.floor(fill_time*2) -- 获取目前 token_bucket 中剩余 token 数 -- 如果是第一次进入,则设置 token_bucket 数量为 令牌桶最大值 local last_tokens = tonumber(redis.call("get", KEYS[1])) if last_tokens == nil then last_tokens = capacity end -- 上一次更新 token_bucket 的时间 local last_refreshed = tonumber(redis.call("get", KEYS[2])) if last_refreshed == nil then last_refreshed = 0 end local delta = math.max(0, now-last_refreshed) -- 通过当前时间与上一次更新时间的跨度,以及生产token的速率,计算出新的token数 -- 如果超过 max_burst,多余生产的token会被丢弃 local filled_tokens = math.min(capacity, last_tokens+(delta*rate)) local allowed = filled_tokens >= requested local new_tokens = filled_tokens if allowed then new_tokens = filled_tokens - requested end -- 更新新的token数,以及更新时间 redis.call("setex", KEYS[1], ttl, new_tokens) redis.call("setex", KEYS[2], ttl, now) return allowed
上述可以看出 lua script
:只涉及对 token 操作,保证 token 生产合理和读取合理。
函数分析
从上述流程中看出:
- 有多重保障机制,保证限流一定会完成。
- 如果
redis limiter
失效,至少在进程内rate limiter
兜底。 - 重试
redis limiter
机制保证尽可能地正常运行。
总结
go-zero
中的 tokenlimit
限流方案适用于瞬时流量冲击,现实请求场景并不以恒定的速率。令牌桶相当预请求,当真实的请求到达不至于瞬间被打垮。当流量冲击到一定程度,则才会按照预定速率进行消费。
但是生产token
上,不能按照当时的流量情况作出动态调整,不够灵活,还可以进行进一步优化。此外可以参考Token bucket WIKI中提到分层令牌桶,根据不同的流量带宽,分至不同排队中。
参考
如果觉得文章不错,欢迎 github 点个star 🤝
同时欢迎大家使用 go-zero

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
云原生应用Go语言:你还在考虑的时候,别人已经应用实践
摘要:在近日于上海召开的第六届Gopher China大会上,华为云微服务首席架构师田晓亮分享了《华为云的Go语言云原生实战经验》,讲述如何构建韧性、高可靠、安全的云原生应用系统,并孵化云原生应用开发框架Go chassis,以提升团队开发效能。 Gopher China作为国内最权威和最实力干货的Go大会,致力于为广大的Gopher提供一线分享交流机会,也为众多一线互联网公司大咖深入探讨Go语言的应用发展提供契机。 在近日于上海召开的第六届Gopher China大会上,华为云微服务首席架构师田晓亮就受邀分享了《华为云的Go语言云原生实战经验》,讲述如何构建韧性、高可靠、安全的云原生应用系统,并孵化云原生应用开发框架Go chassis,以提升团队开发效能。 自华为在2016年成立Cloud BU以来,就引入了Go语言编写的Kubernetes,Prometheus等CNCF项目,华为云的研发团队也开始用Go语言来构建云服务。不过,当时Go的生态并不完善,所以要自己从头到尾编写基础能力模块。 那么,如何用Go构建云服务并将基础能力慢慢建立起来,且听我们慢慢道来。 从一个简单云应用看我...
- 下一篇
带你看懂双亲委派,耗子尾汁
双亲委派机制 双亲委派机制是指当一个类加载器收到一个类加载请求时,该类加载器首先会把请求委派给父类加载器。每个类加载器都是如此(递归的去查找),只有在父类加载器在自己的搜索范围内找不到指定类时,子类加载器才会尝试自己去加载。 显然,在介绍双亲委派机制的时候,不得不提ClassLoader。再说ClassLoader之前,我们得先了解下Java的基本知识。 Java是运行在Java的虚拟机(JVM)中的,但是它是怎么就运行在JVM中了呢?我们在IDE中编写的Java源代码被编译器编译成.class的字节码文件。然后由我们的ClassLoader负责将这些class文件加载到JVM中去执行。 JVM中提供了三层的ClassLoader: 暖冬回血~~~~ 大家好,我是AIO生活,关注我,后续连载更多技术重难点,文章有不足之处,欢迎大家留言指正,谢谢大家啦! 顺便提一下,帮大家整理了些JAVA电子书,扫码回复“1024”获取电子书合集,也可签到领现金红包。 Bootstrap ClassLoader(启动类加载器):主要负责加载核心的类库(java.lang.*等),构造Extensio...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- Docker安装Oracle12C,快速搭建Oracle学习环境
- SpringBoot2全家桶,快速入门学习开发网站教程
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- CentOS关闭SELinux安全模块
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- CentOS6,CentOS7官方镜像安装Oracle11G
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- Hadoop3单机部署,实现最简伪集群