不管是在单体服务中还是在微服务中,开发者为前端提供的API接口都是有访问上限的,当访问频率或者并发量超过其承受范围时候,我们就必须考虑限流来保证接口的可用性或者降级可用性。即接口也需要安装上保险丝,以防止非预期的请求对系统压力过大而引起的系统瘫痪。

go-zero 集成了开箱即用的 限流器 。其中内置了两种限流器,也对应两类使用场景:

种类 原理 场景
periodlimit 单位时间限制访问次数 需要强行限制数据的传输速率
tokenlimit 令牌桶限流 限制数据的平均传输速率,同时允许某种程度的突发传输

本文就来介绍一下 periodlimit

使用

const (
seconds = 1
total = 100
quota = 5
)
// New limiter
l := NewPeriodLimit(seconds, quota, redis.NewRedis(s.Addr(), redis.NodeType), "periodlimit") // take source
code, err := l.Take("first")
if err != nil {
logx.Error(err)
return true
} // switch val => process request
switch code {
case limit.OverQuota:
logx.Errorf("OverQuota key: %v", key)
return false
case limit.Allowed:
logx.Infof("AllowedQuota key: %v", key)
return true
case limit.HitQuota:
logx.Errorf("HitQuota key: %v", key)
// todo: maybe we need to let users know they hit the quota
return false
default:
logx.Errorf("DefaultQuota key: %v", key)
// unknown response, we just let the sms go
return true
}

periodlimit

go-zero 采取 滑动窗口 计数的方式,计算一段时间内对同一个资源的访问次数,如果超过指定的 limit ,则拒绝访问。当然如果你是在一段时间内访问不同的资源,每一个资源访问量都不超过 limit ,此种情况是允许大量请求进来的。

而在一个分布式系统中,存在多个微服务提供服务。所以当瞬间的流量同时访问同一个资源,如何让计数器在分布式系统中正常计数? 同时在计算资源访问时,可能会涉及多个计算,如何保证计算的原子性?

  • go-zero 借助 redisincrby 做资源访问计数
  • 采用 lua script 做整个窗口计算,保证计算的原子性

下面来看看 lua script 控制的几个关键属性:

argument mean
key[1] 访问资源的标示
ARGV[1] limit => 请求总数,超过则限速。可设置为 QPS
ARGV[2] window大小 => 滑动窗口,用 ttl 模拟出滑动的效果
-- to be compatible with aliyun redis,
-- we cannot use `local key = KEYS[1]` to reuse thekey
local limit = tonumber(ARGV[1])
local window = tonumber(ARGV[2])
-- incrbt key 1 => key visis++
local current = redis.call("INCRBY", KEYS[1], 1)
-- 如果是第一次访问,设置过期时间 => TTL = window size
-- 因为是只限制一段时间的访问次数
if current == 1 then
redis.call("expire", KEYS[1], window)
return 1
elseif current < limit then
return 1
elseif current == limit then
return 2
else
return 0
end

至于上述的 return code ,返回给调用方。由调用方来决定请求后续的操作:

return code tag call code mean
0 OverQuota 3 over limit
1 Allowed 1 in limit
2 HitQuota 2 hit limit

下面这张图描述了请求进入的过程,以及请求触发 limit 时后续发生的情况:

后续处理

如果在服务某个时间点,请求大批量打进来,periodlimit 短期时间内达到 limit 阈值,而且设置的时间范围还远远没有到达。后续请求的处理就成为问题。

periodlimit 中并没有处理,而是返回 code 。把后续请求的处理交给了开发者自己处理。

  1. 如果不做处理,那就是简单的将请求拒绝
  2. 如果需要处理这些请求,开发者可以借助 mq 将请求缓冲,减缓请求的压力
  3. 采用 tokenlimit,允许暂时的流量冲击

所以下一篇我们就来聊聊 tokenlimit

总结

go-zero 中的 periodlimit 限流方案是基于 redis 计数器,通过调用 redis lua script ,保证计数过程的原子性,同时保证在分布式的情况下计数是正常的。

但是这种方案也存在缺点,因为它要记录时间窗口内的所有行为记录,如果这个量特别大的时候,内存消耗会变得非常严重。

参考

同时欢迎大家使用 go-zero 并加入我们,https://github.com/tal-tech/go-zero

如果觉得文章不错,欢迎github点个star

项目地址:

https://github.com/tal-tech/go-zero

go-zero 如何扛住流量冲击(一)的更多相关文章

  1. go-zero 如何扛住流量冲击(二)

    本篇文章承接上一篇go-zero 如何扛住流量冲击(一). 上一篇介绍的是 go-zero 中滑动窗口限流,本篇介绍另外一个 tokenlimit ,令牌桶限流. 使用 const ( burst = ...

  2. 扛住阿里双十一高并发流量,Sentinel是怎么做到的?

    Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景 本文介绍阿里开源限流熔断方案Sentinel功能.原理.架构.快速入门以及相关框架比较 基本介绍 1 名词解释 服务限流 :当系 ...

  3. 阿里P8面试官:如何设计一个扛住千万级并发的架构?

    大家先思考一个问题,这也是在面试过程中经常遇到的问题. 如果你们公司现在的产品能够支持10W用户访问,你们老板突然和你说,融到钱了,会大量投放广告,预计在1个月后用户量会达到1000W,如果这个任务交 ...

  4. Linux性能优化实战学习笔记:第三十九讲

    一.上节回顾 上一节,我带你学习了 tcpdump 和 Wireshark 的使用方法,并通过几个案例,带你用这两个工具实际分析了网络的收发过程.碰到网络性能问题,不要忘记可以用 tcpdump 和W ...

  5. CDN百科 | 最近,你的APP崩了吗?

    过去几个月里,#xxx崩了#这个话题频繁出现在热搜榜上,让不少程序员小哥哥瑟瑟发抖. 从疫情宅家时期著名的视频APP“三连崩”,到全面复工开课后的在线教育平台与办公软件频繁宕机,再到报复性消费引发的点 ...

  6. Kafka万亿级消息实战

    一.Kafka应用 本文主要总结当Kafka集群流量达到 万亿级记录/天或者十万亿级记录/天  甚至更高后,我们需要具备哪些能力才能保障集群高可用.高可靠.高性能.高吞吐.安全的运行. 这里总结内容主 ...

  7. Kafka 负载均衡在 vivo 的落地实践

    ​vivo 互联网服务器团队-You Shuo 副本迁移是Kafka最高频的操作,对于一个拥有几十万个副本的集群,通过人工去完成副本迁移是一件很困难的事情.Cruise Control作为Kafka的 ...

  8. Kafka 万亿级消息实践之资源组流量掉零故障排查分析

    作者:vivo 互联网服务器团队-Luo Mingbo 一.Kafka 集群部署架构 为了让读者能与小编在后续的问题分析中有更好的共鸣,小编先与各位读者朋友对齐一下我们 Kafka 集群的部署架构及服 ...

  9. 170331、58到家MQ如何快速实现流量削峰填谷

    问:为什么会有本文? 答:上一篇文章<到底什么时候该使用MQ?>引起了广泛的讨论,有朋友回复说,MQ的还有一个典型应用场景是缓冲流量,削峰填谷,本文将简单介绍下,MQ要实现什么细节,才能缓 ...

随机推荐

  1. Python+Appium自动化测试(8)-swipe()滑动页面

    app自动化测试过程中,经常会遇到滑动屏幕操作,appium框架的话我们可以使用webdriver提供的swipe()方法来对屏幕页面进行上滑.下滑.左滑.右滑操作. 一,swipe方法介绍 swip ...

  2. iot平台

    iot平台 iot平台卓岚云是一个免费的物联网云平台,用户只需按步骤完成注册即可免费使用卓岚云 .支持PC.Android.iOS多平台终端.用户可以在任何地方远程访问任何地方的串口设备,并对远程设备 ...

  3. spring boot:接收数组参数及多文件混合json参数(spring boot 2.3.4)

    一,生产环境中的复杂参数上传的场景 1,保存排序值 : 例如:某一件商品的多张展示图片排序,提交的排序值要和图片的id相对应 2,上传多张图片,图片要和指定的变量相对应 例如:在添加商品sku时, 需 ...

  4. OSI七层网络

    7-应用层 各种应用软件 6-表示层 转换.加密.解密压缩 5-会话层 建立会话,保证会话,终止会话 4-传输层 TCP/UDP 3-网络层 路由 2-数据链路层 交换 1-物理层

  5. 51nod 最大M子段和系列(1052、1053、1115)

    51nod1052 数据量小,可使用O(N*M)的DPAC,递推公式: dp[i][j]=max(dp[i-1][j-1], dp[i][j-1])+a[j]; dp[i][j]表示前j个数取 i 段 ...

  6. 解析SparkStreaming和Kafka集成的两种方式

    spark streaming是基于微批处理的流式计算引擎,通常是利用spark core或者spark core与spark sql一起来处理数据.在企业实时处理架构中,通常将spark strea ...

  7. VirtualXposed结合justTrustMe 模块傻瓜式破解app没法抓包问题

    一.首先就是按照这两个apk 声明仅供学习 justTrustMe 链接:https://pan.baidu.com/s/1av3oaez4y4n6a9C1I0VsAg 提取码:mjqg Virtua ...

  8. LeakCanary检测内存泄漏

    内存泄漏原因: 线程造成的内存泄漏 Handler造成的内存泄漏 单例导致内存泄露 静态变量导致内存泄露 非静态内部类导致内存泄露 未取消注册(BroadcastReceiver )或回调导致内存泄露 ...

  9. Redis学习笔记(二)——Keys通用操作

    1.查询所有key: keys * 2.*通用符(代表0或多),查询所有以n(*)开头的key: keys n* 3.?通用符(代表1个字符): key n? 4.del删除key [key1 key ...

  10. JUC---13各种锁

    一.公平锁与非公平锁 公平锁:加锁前检查是否有排队等待的线程,优先排队等待的线程,先来先得 非公平锁:加锁时不考虑排队等待问题,直接尝试获取锁,获取不到自动到队尾等待 非公平锁性能比公平锁高5~10倍 ...