go-zero源码阅读-固定时间窗口限流

一. 简介

go-zero框架的PeriodLimit限流是固定时间窗口限流,通过lua脚本操作redis, 对key进行计数并搭配过期时间实现限流

二. 源码

1. 代码路径

core/limit/periodlimit.go

2.  核心struct

type (
	// PeriodOption defines the method to customize a PeriodLimit.
    // PeriodLimit进行参数设置
	PeriodOption func(l *PeriodLimit)

	// A PeriodLimit is used to limit requests during a period of time.
	PeriodLimit struct {
        // 时间窗口大小,单位: 秒
		period     int
        // 限流阈值
		quota      int
        // 依赖redis实现
		limitStore *redis.Redis
        // key前缀
		keyPrefix  string
		align      bool
	}
)

结构体字段围绕redis的set命令设计, align 为false时,时间窗口是固定的值period, 如果为true则是周期性的(如period=3时: 3->2->1->3->2。。。)

3. 限流方法

// Take requests a permit, it returns the permit state.
func (h *PeriodLimit) Take(key string) (int, error) {
	return h.TakeCtx(context.Background(), key)
}

// TakeCtx requests a permit with context, it returns the permit state.
func (h *PeriodLimit) TakeCtx(ctx context.Context, key string) (int, error) {
// 执行lua脚本进行限流判断
// ARGV为限流阈值和周期
	resp, err := h.limitStore.EvalCtx(ctx, periodScript, []string{h.keyPrefix + key}, []string{
		strconv.Itoa(h.quota),
   // 调用方法获取限流周期
		strconv.Itoa(h.calcExpireSeconds()),
	})
	if err != nil {
		return Unknown, err
	}

// 判断限流结果
	code, ok := resp.(int64)
	if !ok {
		return Unknown, ErrUnknownCode
	}

	switch code {
	case internalOverQuota:
		return OverQuota, nil
	case internalAllowed:
		return Allowed, nil
	case internalHitQuota:
		return HitQuota, nil
	default:
		return Unknown, ErrUnknownCode
	}
}

核心逻辑就是执行lua脚本进行限流检测,最后校验方法返回值,判断是否限流

4. lua脚本


const periodScript = `local limit = tonumber(ARGV[1]) // 获取阈值
local window = tonumber(ARGV[2]) // 获取限流窗口
local current = redis.call("INCRBY", KEYS[1], 1) // 限流计数, key 加1
// 只有值为1的时候,进行过期时间设置 
// 通过过期时间 控制限流窗口,key过期后,重新计数,进入新的窗口
if current == 1 then 
    redis.call("expire", KEYS[1], window)
end
// 限流阈值判断
if current < limit then
    return 1
elseif current == limit then
    return 2
else
    return 0
end`

通过对key进行计数,key的值为1时,设置过期时间,来达到固定时间窗口限流的目的

5. 初始化配置设置

type (
	// PeriodOption defines the method to customize a PeriodLimit.
    // 参数设置 函数
	PeriodOption func(l *PeriodLimit)
)

func NewPeriodLimit(period, quota int, limitStore *redis.Redis, keyPrefix string,
	opts ...PeriodOption) *PeriodLimit {
	limiter := &PeriodLimit{
		period:     period,
		quota:      quota,
		limitStore: limitStore,
		keyPrefix:  keyPrefix,
	}

// 传入函数后,一一执行,由外部控制参数设置
	for _, opt := range opts {
		opt(limiter)
	}

	return limiter
}

// 参数设置
func Align() PeriodOption {
	return func(l *PeriodLimit) {
		l.align = true
	}
}

由初始化方法传入 多个配置设置函数进行配置设置, 由外部控制配置设置,增加了初始化配置的灵活性

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
go-zero是一个开的Go语言框架,它在构建微服务和高并发应用方面具有突破性的优势。其中一个突出的特点就是它整合了masterminds/squirrel,从而实现了优雅的多数据库支持。 masterminds/squirrel是一个流行的SQL查询构建器,它以非常直观和灵活的方式提供了编写SQL查询的功能。而go-zero在此基础上做了进一步的封装和优化,使得使用者能够更加方便地编写和执行SQL查询。 首先,go-zero提供了一组简洁而强大的API,使得构建SQL查询非常容易。开发者只需要按照一定的约定来创建查询参数和条件,然后使用go-zero提供的API来构建查询语句,即可完成复杂的SQL查询。 其次,go-zero还增加了一些高级功能,进一步提升了多数据库查询的灵活性和性能。例如,它支持数据库连接池管理,可以动态调整数据库连接数以适应并发请求;还支持分表分库功能,可以按照一定的规则将数据分散存储在不同的数据库或表中,从而提高查询效率。 最重要的是,go-zero通过内置的代码生成工具,提供了自动化生成数据库访问代码的能力。开发者只需要定义数据表的结构,然后运行代码生成工具,就能够自动生成包含增删改查等一系列数据库操作的代码。这极大地提高了开发效率,减少了出错的机会。 综上所述,go-zero整合了masterminds/squirrel,通过提供简洁强大的API、高级功能和自动化代码生成工具,实现了优雅的多数据库支持。它在微服务和高并发应用场景下的表现突出,为开发者提供了极大的便利和效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值