限量核心代码:limit.go
type Limit struct {
Name string
Key string
Rate int64
Max int64
Default int64
}
func (l *Limit) Add(a, b float64) float64 {
return a + b
}
// 令牌 Unit is `qps/seconds`
// rate 允许多大的请求访问,请求数/秒
// max 最大的请求数
// defaults 初始化请求数
// return 1验证通过,0被限量
func (l *Limit) getLimit(config *Limit) int {
name := config.Name
key := config.Key
now := time.Now().Unix()
sKey := GetStorekey(name, key)
nKey := GetNextTimeKey(name, key)
rate := config.Rate
max := config.Max
defaults := config.Default
if luaScript == "" {
logs.Error("load lua script error")
return 0
}
result2, err := common.RedisLimitClient.ScriptLoad(luaScript).Result() //返回的脚本会产生一个sha1哈希值,下次用的时候可以直接使用这个值
if err != nil {
logs.Error("fillLimit-GetLimit-ScriptLoad-err", err)
return 0
}
foo := common.RedisLimitClient.EvalSha(result2, []string{sKey, nKey}, now, rate, max, defaults)
result, err := foo.Int()
if err != nil {
logs.Error("fillLimit-GetLimit-EvalSha-err", err)
return 0
}
return result
}
使用的lua脚本: limit.lua
local sKey = KEYS[1];
local nKey = KEYS[2];
local now = tonumber(ARGV[1]);
local rate = tonumber(ARGV[2]);
local max = tonumber(ARGV[3]);
local default = tonumber(ARGV[4]);
local sNum = redis.call('get', KEYS[1]);
if((not sNum) or sNum == nil)
then
sNum = 0
end
sNum = tonumber(sNum);
local nNum = redis.call('get', KEYS[2]);
if((not nNum) or nNum == nil)
then
nNum = now
sNum = default
end
nNum = tonumber(nNum);
local newPermits = 0;
if(now > nNum)
then
newPermits = (now-nNum)*rate+sNum;
sNum = math.min(newPermits, max)
end
local isPermited = 0;
if(sNum > 0)
then
sNum = sNum -1;
isPermited = 1;
else
sNum = 0;
isPermited = 0;
end
redis.call('set', KEYS[1], sNum);
redis.call('set', KEYS[2], now);
return isPermited
初始化时加载 lua脚本:loadLua.go
var luaScript = ""
func init() {
loadLua()
}
func loadLua() {
luaPath := "models/fill/limit/limit.lua"
luaFile, err := filepath.Abs(luaPath)
if err != nil {
logs.Error("fill-limit.lua", err)
return
}
luaScriptByte, err := os.ReadFile(luaFile)
if err != nil {
logs.Error("fill-ReadFile-limit.lua", err)
return
}
luaScript = string(luaScriptByte)
}
定义redis中使用的key:keys.go
// GetLimitKey 默认限流key
func GetLimitKey() string {
return "{fill:limit}"
}
// GetStorekey 获取令牌桶key
func GetStorekey(name string, key string) string {
return fmt.Sprintf("%s:%s:store", name, key)
}
func GetNextTimeKey(name string, key string) string {
return fmt.Sprintf("%s:%s:next", name, key)
}
入口测试:main.go
func main() {
// 限流
config := &Limit{
Name: GetLimitKey(),
Key: "suibian",
Rate: 1,
Max: 6,
Default: 5,
}
for i := 0; i < 10; i++ {
result := config.GetLimit(config)
println(result)
}
}