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 在自己的网站上.渐渐地,二狗子的网站因为文章质量高,技术分享全面,受到了很 ...
随机推荐
- soct的创建方法
服务器端:ServerSocket提供的实例 ServerSocket server = new ServerSocket(端口号) 客户端:Socket提供的实例 Socket client = ...
- windows18.04远程桌面连接ubuntu16.04
方法1: https://www.cnblogs.com/xuliangxing/p/7642650.html 方法2: 也可以通过在ubuntu上安装samba.
- 在嵌入式设备中实现webrtc的第三种方式①
最近两年,我对于网络知识,包括底层协议学习比较多,webrtc这种几乎是使用到了所有层面网络协议的技术也逐渐进入我的视野. 之前我提出了两种在嵌入式设备上实现webrtc的方式,一是用官方代码,然后改 ...
- [分享] 通过修改CSS自定义chrome滚动条样式
首先得说一句 我不懂CSS的写法之类的 这段CSS也是在网上找的 所以有更先进的需求的话 我肯定不能满足你们了 不好意思效果图在10楼有人上了 我这边不管怎么弄 上传图片都卡在96% 而且不翻wall ...
- 导出excel带合并单元格方法的Demo
package com.test.util; import java.io.FileNotFoundException; import java.io.FileOutputStream; import ...
- MySQL全面瓦解4:数据定义-DDL
前言 SQL的语言分类主要包含如下几种: DDL 数据定义语言 create.drop.alter 数据定义语言 create.drop.alter 语句 . DML 数据操纵语言 insert.de ...
- Luogu P4234 最小差值生成树
题意 给定一个 \(n\) 个点 \(m\) 条边的有权无向图,求出原图的一棵生成树使得该树上最大边权与最小边权的差值最小. \(\texttt{Data Range:}1\leq n\leq 5\t ...
- 关于Android Studio中使用jni进行opencv配置开发环境的要素秘诀
使用jni进行opencv开发可以快速地将PC端的opencv代码移植到手机上,但是如何在android studio下进行配置,网上几乎找不到教程,大多都是eclipse下使用mk文件的方法,找不到 ...
- Matlab批量绘制图像并保存
author:ZKe ------------------------------- 以下是一个txt文件,每行11个字段,第一个字段是日期,后面10个是用户id和对应今天发表微博数,所有字段用制表符 ...
- js 图片放大镜功能
原理:放置两张相同的图片,一张作为主图片(图片1),另一张作为用来裁剪并放大的图片(图片2) 鼠标移动时,计算鼠标在图片1的位置(距离图片1左上角的x,y距离),以此决定在图片2开始 ...