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 如何扛住流量冲击(二)的更多相关文章
- go-zero 如何扛住流量冲击(一)
不管是在单体服务中还是在微服务中,开发者为前端提供的API接口都是有访问上限的,当访问频率或者并发量超过其承受范围时候,我们就必须考虑限流来保证接口的可用性或者降级可用性.即接口也需要安装上保险丝,以 ...
- 扛住阿里双十一高并发流量,Sentinel是怎么做到的?
Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景 本文介绍阿里开源限流熔断方案Sentinel功能.原理.架构.快速入门以及相关框架比较 基本介绍 1 名词解释 服务限流 :当系 ...
- 阿里P8面试官:如何设计一个扛住千万级并发的架构?
大家先思考一个问题,这也是在面试过程中经常遇到的问题. 如果你们公司现在的产品能够支持10W用户访问,你们老板突然和你说,融到钱了,会大量投放广告,预计在1个月后用户量会达到1000W,如果这个任务交 ...
- ECS主动运维事件--让你HOLD住全场 (二)
背景 数月前,我们推出了新的功能:ECS主动运维事件--让你HOLD住全场 https://yq.aliyun.com/articles/573782?spm=a2c4e.11155435.0.0.7 ...
- Linux性能优化实战学习笔记:第三十九讲
一.上节回顾 上一节,我带你学习了 tcpdump 和 Wireshark 的使用方法,并通过几个案例,带你用这两个工具实际分析了网络的收发过程.碰到网络性能问题,不要忘记可以用 tcpdump 和W ...
- Kafka万亿级消息实战
一.Kafka应用 本文主要总结当Kafka集群流量达到 万亿级记录/天或者十万亿级记录/天 甚至更高后,我们需要具备哪些能力才能保障集群高可用.高可靠.高性能.高吞吐.安全的运行. 这里总结内容主 ...
- Kafka 负载均衡在 vivo 的落地实践
vivo 互联网服务器团队-You Shuo 副本迁移是Kafka最高频的操作,对于一个拥有几十万个副本的集群,通过人工去完成副本迁移是一件很困难的事情.Cruise Control作为Kafka的 ...
- CDN百科 | 最近,你的APP崩了吗?
过去几个月里,#xxx崩了#这个话题频繁出现在热搜榜上,让不少程序员小哥哥瑟瑟发抖. 从疫情宅家时期著名的视频APP“三连崩”,到全面复工开课后的在线教育平台与办公软件频繁宕机,再到报复性消费引发的点 ...
- 第一次亲密接触——二狗子初识 CDN
二狗子是国内知名XXX大学的在校学生,作为一名编程爱好者,他利用业余时间搭建了一个网站,把平时的学习心得和技术分享全都 PO 在自己的网站上.渐渐地,二狗子的网站因为文章质量高,技术分享全面,受到了很 ...
随机推荐
- mysql优化篇(基于索引)
在上一篇文章:Mysql索引(一篇就够le) 中介绍了索引的基本使用,分类和原理,也强烈建议先读Mysql索引(一篇就够le),然后继续本文的阅读 我们也知道mysql的优化可以从很多的方面进行,比如 ...
- BMP位图调色板说明
网上一搜,可以看到BMP位图结构的详细说明,这篇文章专门谈一下其中的调色板. 多少位位图并不是指每一个颜色该用多少位表示,对于颜色来说,它始终都是24位(RGB),或者是32位(RGBA),而是指该位 ...
- 攻防世界-mfw
打开题目,让我们看看about这个链接是什么,我们看到了这个 他说他写这个站点用了git.php.bootstrap这很容易就能让我们想到,git源码泄露,这我们直接掏出githack, python ...
- Swagger配置与使用
问题:前后端分离时代的到来 前端需要测试后端数据 后端提供接口,实时更新接口的改动 一.Swagger简介 号称世界上最流行的api框架 Restful api文档在线自动生成工具-->api文 ...
- C. k-Amazing Numbers 解析(思維)
Codeforce 1417 C. k-Amazing Numbers 解析(思維) 今天我們來看看CF1417C 題目連結 題目 略,請直接看原題. 前言 我實作好慢... @copyright p ...
- web worker的介绍和使用
目录 简介 Web Workers的基本概念和使用 Web Workers的分类 worker和main thread之间的数据传输 简介 什么是web worker呢?从名字上就可以看出,web w ...
- Java安全框架(一)Spring Security
Java安全框架(一)Spring Security 文章主要分三部分 1.Spring Security的架构及核心组件:(1)认证:(2)权限拦截:(3)数据库管理:(4)权限缓存:(5)自定 ...
- 《Web接口开发与自动化测试》学习笔记(三)
一.认证系统 使用django本身自带的认证系统 1.登录admin后台 1. 先建立一个管理员用户: > python manage.py creatsuperuser 输入用户名.邮箱和密码 ...
- C++ 数据结构 1:线性表
1 数据结构 1.1 数据结构中基本概念 数据:程序的操作对象,用于描述客观事物. 数据的特点: 可以输入到计算机 可以被计算机程序处理 数据是一个抽象的概念,将其进行分类后得到程序设计语言中的类型. ...
- npm的命令参数 --save-dev和 --save两者有什么区别?
我们在安装npm包的时候经常会遇到 --save-dev 和 --save 这两个命令参数,两个命令都是往package.json文件里写入信息,两者有什么区别呢? 1. --save 会把依赖包名称 ...