go-zero 如何扛住流量冲击(一)

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: go-zero 如何扛住流量冲击(一)

不管是在单体服务中还是在微服务中,开发者为前端提供的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 时后续发生的情况:

317e6133b8d4f088ddb23e6164560e8a.png

后续处理

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

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

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

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

总结

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

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

参考

  • go-zero periodlimit
  • 分布式服务限流实战,已经为你排好坑了

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

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
2月前
|
Go UED
Go中的流量限制:有效控制流量
Go中的流量限制:有效控制流量
|
2月前
|
NoSQL Redis 开发者
go-zero 如何扛住流量冲击(二)
go-zero 如何扛住流量冲击(二)
|
5月前
|
SQL Go 数据库
【Sentinel Go】新手指南、流量控制、熔断降级和并发隔离控制
【2月更文挑战第12天】随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式、多语言异构化服务架构的流量治理组件,主要以流量为切入点,从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度来帮助开发者保障微服务的稳定性。
153 0
|
Dubbo 网络协议 Cloud Native
涂鸦智能 dubbo-go 亿级流量的实践与探索
dubbo 是一个基于 Java 开发的高性能的轻量级 RPC 框架,dubbo 提供了丰富的服务治理功能和优秀的扩展能力。而 dubbo-go 在 java 与 golang 之间提供统一的服务化能力与标准,是涂鸦智能目前最需要解决的主要问题。
涂鸦智能 dubbo-go 亿级流量的实践与探索
|
Dubbo 网络协议 Cloud Native
涂鸦智能 dubbo-go 亿级流量的实践与探索
dubbo 是一个基于 Java 开发的高性能的轻量级 RPC 框架,dubbo 提供了丰富的服务治理功能和优秀的扩展能力。而 dubbo-go 在 java 与 golang 之间提供统一的服务化能力与标准,是涂鸦智能目前最需要解决的主要问题。
涂鸦智能 dubbo-go 亿级流量的实践与探索
|
6天前
|
Go
Go 语言循环语句
在不少实际问题中有许多具有规律性的重复操作,因此在程序中就需要重复执行某些语句。
14 1
|
5天前
|
Go 开发者
探索Go语言的并发之美
在Go语言的世界里,"并发"不仅仅是一个特性,它是一种哲学。本文将带你领略Go语言中goroutine和channel的魔力,揭示如何通过Go的并发机制来构建高效、可靠的系统。我们将通过一个简单的示例,展示如何利用Go的并发特性来解决实际问题,让你的程序像Go一样,轻盈而强大。
|
6天前
|
JSON Go API
使用Go语言和Gin框架构建RESTful API:GET与POST请求示例
使用Go语言和Gin框架构建RESTful API:GET与POST请求示例
|
6天前
|
Go
go语言创建字典
go语言创建字典
下一篇
无影云桌面