版权声明 本站原创文章 由 萌叔 发表
转载请注明 萌叔 | http://vearne.cc

总述

这个库的目标是为了能够简单粗暴的实现分布式限频功能, 类似于ID生成器的用法,
client每次从Redis拿回-批数据(在这里只是一个数值)进行消费,
只要没有消费完,就没有达到频率限制。

优势

  • 依赖少,只依赖redis,不需要专门的服务
  • 使用的redis自己的时钟,不需要相应的服务器时钟完全一致
  • 线程(协程)安全
  • 系统开销小,对redis的压力很小

安装

go get github.com/vearne/ratelimit

用法

1. 创建 redis.Client

依赖 “github.com/go-redis/redis”

    client := redis.NewClient(&redis.Options{
        Addr:     "localhost:6379",
        Password: "xxx", // password set
        DB:       0,  // use default DB
    })

2. 创建限频器

    limiter, _ := ratelimit.NewRedisRateLimiter(client,
        "push",
        1 * time.Second,
        200,
        10,
        ratelimit.TokenBucketAlg,
    )

表示允许每秒操作200次

    limiter, _ := ratelimit.NewRedisRateLimiter(client,
        "push",
        1 * time.Minute,
        200,
        10,
        ratelimit.TokenBucketAlg,
    )

表示允许每分钟操作200次

函数原型如下:

func NewRedisRateLimiter(client *redis.Client, key string,
    duration time.Duration, throughput int, batchSize int, alg int) (*RedisRateLimiter, error)
参数 说明
key redis中key
duration 表明在duration时间间隔内允许操作throughput次
throughput 表明在duration时间间隔内允许操作throughput次
batchSize 每次从redis拿回的可用操作的数量
alg 可选的算法, 目前的合法值为 ratelimit.TokenBucketAlg : “令牌桶算法” ratelimit.CounterAlg : “计数器算法”

注意
频率 = throughput / duration
另外为了保证性能足够高,duration的最小精度是秒

完整例子

package main

import (
    "fmt"
    "github.com/go-redis/redis"
    "math/rand"
    "github.com/vearne/ratelimit"
    "sync"
    "time"
)

func consume(r *ratelimit.RedisRateLimiter, group *sync.WaitGroup) {
    for {
        if r.Take() {
            group.Done()
        } else {
            time.Sleep(time.Duration(rand.Intn(10)+1) * time.Millisecond)
        }
    }
}

func main() {
    client := redis.NewClient(&redis.Options{
        Addr:     "localhost:6379",
        Password: "xxxxx", // password set
        DB:       0,       // use default DB
    })

    limiter, _ := ratelimit.NewRedisRateLimiter(client,
        "push",
        1*time.Second,
        100,
        10,
        //ratelimit.CounterAlg,
        ratelimit.TokenBucketAlg,
    )

    var wg sync.WaitGroup
    total := 5000
    wg.Add(total)
    start := time.Now()
    for i := 0; i < 100; i++ {
        go consume(limiter, &wg)
    }
    wg.Wait()
    cost := time.Since(start)
    fmt.Println("cost", time.Since(start), "rate", float64(total)/cost.Seconds())
}

依赖

go-redis/redis

致谢

模块的开发受到了资料1的启发,在此表示感谢

资料

  1. 性能百万/s:腾讯轻量级全局流控方案详解

如果我的文章对你有帮助,你可以给我打赏以促使我拿出更多的时间和精力来分享我的经验和思考总结。

微信支付码

发表评论

电子邮件地址不会被公开。

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据