Redis 调用
本章介绍如何在插件中调用 Redis、本地开发环境搭建、以及开发基于令牌桶限流插件。
1 Redis 调用
Higress 插件的 Go SDK 中 redis_wrapper.go 封装 Redis 调用, 部分核心代码如下:
所有调用 Redis 的接口,最终通过 RedisCall 调用 Redis, 同时回调 RedisResponseCallback 回调函数。
2 令牌桶限流
常见的限流算法有固定窗口限流算法、滑动窗口限流算法、漏桶限流算法、令牌桶限流算法等。这里主要介绍令牌桶限流算法。 令牌桶算法原理:
- 令牌以固定的频率被添加到令牌桶中。
- 如果令牌数量满了,超过令牌桶容量的限制,那就丢弃。
- 系统在接受到一个用户请求时,都会先去令牌桶要一个令牌。如果拿到令牌,那么就处理这个请求的业务逻辑。
- 如果拿不到令牌,就直接拒绝这个请求。
令牌桶算法允许一定量的突发请求,因为桶可以存储一定数量的令牌,从而在短期内处理更多的请求。具体原理见下图:
关于 QPS 限流算法和令牌桶算法两种限流算法优缺点,可以参考:限流算法选择。
3 本地开发环境搭建
3.1 初始化工程目录
- 新建一个工程目录文件 cluster-bucket-limit。
- 在所建目录下执行以下命令,初始化 Go 工程。
更详细信息参考第十四章 Wasm 插件介绍和开发自定义插件。
3.2 Makefile、Dockerfile、docker-compose.yaml、envoy.yaml 文件
- Makefile、Dockerfile
Makefile、Dockerfile 文件参考第十四章 Wasm 插件介绍和开发自定义插件。
- docker-compose.yaml
- envoy.yaml 文件
envoy.yaml 配置文件如下:
envoy.yaml 配置文件增加了 outbound|6379||redis.static
集群,用于连接 Redis 服务。Redis 连接目前不支持 DNS 域名配置,只支持 IP 地址配置。
因此这里 Redis 的 IP 地址是 172.20.0.100
。
3 令牌桶限流插件开发
3.1 插件配置和配置解析
插件配置和配置解析部分核心代码如下:
这里忽略插件配置解析的细节,主要显示 Redis 解析配置。可以看出这里需要调用 wrapper.NewRedisClusterClient
方法初始化 RedisClient 和 RedisClient Init
方法初始化 Redis 连接。
3.2 插件限流 Lua 脚本
令牌桶限流的 Lua 脚本如下:
Lua 脚本是在 Redis 中执行的,用于实现令牌桶限流算法。下面是对脚本参数和原理的分析:
- 参数解释:
- KEYS[1] 和 KEYS[2]:这两个参数是通过 Redis 调用传递的键(keys),通常用于存储令牌桶的当前令牌数(tokens_key)和最后刷新时间(timestamp_key)。
- ARGV[1] 到 ARGV[5]:这些参数是通过 Redis 调用传递的参数,用于配置限流策略。
- rate:单位时间内生成的令牌数量。
- capacity:令牌桶的容量,即最多可以容纳的令牌数。
- now:当前时间,通常以时间戳表示,以秒单位。
- requested:当前请求需要的令牌数。
- unit:令牌生成的时间单位,1 表示秒,60 表示分钟,3600 表示小时。
- 脚本原理:
- 初始化变量:根据传入的参数初始化令牌桶的填充时间和 TTL(生存时间)。
- 获取当前状态:从 Redis 中获取当前的令牌数(last_tokens)和最后刷新时间(last_refreshed)。如果不存在,则初始化为令牌桶的容量。
- 计算令牌填充:
- delta:自上次刷新以来经过的单位时间。
- filled_tokens:根据 delta 和 rate 计算应该填充的令牌数,但不能超过桶的容量。
- 判断是否允许请求:
- allowed:如果当前令牌数加上填充的令牌数大于等于请求的令牌数,则允许请求。
- 更新令牌数:
- 如果请求被允许,从当前令牌数中减去请求的令牌数,更新 new_tokens。
- 设置新的状态:
- 如果 TTL 大于 0,则更新 Redis 中的令牌数和时间戳,设置新的 TTL。
- 返回结果:
- 返回一个包含限流结果的数组,包括是否允许请求、新的令牌数、桶容量和 TTL。
3.3 插件限流具体实现
限流主要实现在插件 onHttpRequestHeaders
方法中,部分核心代码如下:
onHttpRequestHeaders 函数的核心逻辑可以概括为以下几个步骤:
- 获取 Tokens:根据配置(config.InHeader 或 config.InQuery),从请求头或查询参数中提取用于限流的 tokens 信息。
- 验证 Tokens:检查提取的 tokens 是否存在且数量合理(不能多于一个),如果不符合要求,返回相应的错误处理。
- 查找限流项:使用提取的 tokens 查找配置中的限流规则(LimitItem),如果没有找到适用的限流规则,则允许请求继续。
- 执行限流逻辑:如果找到限流规则,构建 Redis 脚本需要的键和参数,然后调用 Redis 脚本执行限流算法。
- 处理 Redis 脚本结果:根据 Redis 脚本返回的结果,创建 LimitContext 对象并根据算法结果决定是否允许请求继续:
- 如果请求被拒绝(context.Allowed <= 0),执行限流逻辑并通知客户端。
- 如果请求被允许,将 LimitContext 对象存储在 HttpContext 中,供后续处理使用。
4 测试和验证
- 正常流量
可以看到请求被允许,并且返回了相应的响应。
- 触发流控
参考
- [1] 限流算法选择