Spaces:
Build error
Build error
| package limiter | |
| import ( | |
| "context" | |
| _ "embed" | |
| "fmt" | |
| "github.com/go-redis/redis/v8" | |
| "one-api/common" | |
| "sync" | |
| ) | |
| //go:embed lua/rate_limit.lua | |
| var rateLimitScript string | |
| type RedisLimiter struct { | |
| client *redis.Client | |
| limitScriptSHA string | |
| } | |
| var ( | |
| instance *RedisLimiter | |
| once sync.Once | |
| ) | |
| func New(ctx context.Context, r *redis.Client) *RedisLimiter { | |
| once.Do(func() { | |
| // 预加载脚本 | |
| limitSHA, err := r.ScriptLoad(ctx, rateLimitScript).Result() | |
| if err != nil { | |
| common.SysLog(fmt.Sprintf("Failed to load rate limit script: %v", err)) | |
| } | |
| instance = &RedisLimiter{ | |
| client: r, | |
| limitScriptSHA: limitSHA, | |
| } | |
| }) | |
| return instance | |
| } | |
| func (rl *RedisLimiter) Allow(ctx context.Context, key string, opts ...Option) (bool, error) { | |
| // 默认配置 | |
| config := &Config{ | |
| Capacity: 10, | |
| Rate: 1, | |
| Requested: 1, | |
| } | |
| // 应用选项模式 | |
| for _, opt := range opts { | |
| opt(config) | |
| } | |
| // 执行限流 | |
| result, err := rl.client.EvalSha( | |
| ctx, | |
| rl.limitScriptSHA, | |
| []string{key}, | |
| config.Requested, | |
| config.Rate, | |
| config.Capacity, | |
| ).Int() | |
| if err != nil { | |
| return false, fmt.Errorf("rate limit failed: %w", err) | |
| } | |
| return result == 1, nil | |
| } | |
| // Config 配置选项模式 | |
| type Config struct { | |
| Capacity int64 | |
| Rate int64 | |
| Requested int64 | |
| } | |
| type Option func(*Config) | |
| func WithCapacity(c int64) Option { | |
| return func(cfg *Config) { cfg.Capacity = c } | |
| } | |
| func WithRate(r int64) Option { | |
| return func(cfg *Config) { cfg.Rate = r } | |
| } | |
| func WithRequested(n int64) Option { | |
| return func(cfg *Config) { cfg.Requested = n } | |
| } | |