redis实现的简单令牌桶

这里给出的令牌桶是以redis单节点为中间件, 改成以redis集群为中间件应该也很简单. 不过, 这里的实现比较简单, 主要提供两个函数, 一个用于消费令牌, 一个用于添加令牌. 这里, 消费令牌和添加令牌都是通过lua来保证原子性.

消费令牌的代码如下 :

// FetchToken 用来获取某个key的一个令牌
func (acc *Accessor) FetchToken(key string) (bool, error) {
    /*
     * KEYS[1] 表示特定的key, 这个key是当前的令牌数
     */
    keyFetchScript :=
        `--[[测试显示, 通过call, 可以将error返回给客户端, 即使没有使用return]]--
        local curNum = redis.call("DECR", KEYS[1])
        if (curNum >= 0)
        then
            return true
        end

        redis.call("INCR", KEYS[1])
        return false
        `

    keyFetchCmd := redis.NewScript(keyFetchScript)
    res, err := keyFetchCmd.Run(acc.client, []string{key}).Result()
    if err != nil && err != redis.Nil {
        return false, err
    }

    if err == redis.Nil {
        return false, nil
    }

    if val, ok := res.(int64); ok {
        return (val == 1), nil
    }

    return false, errors.New("res should be bool")
}

这里每一个key都有一个辅助的key_idx, 每次增加key的令牌数, 都会使key_idx的值加1, 同时这个函数调用会返回对应的key_idx的值. 如果传入的idx的值与key_idx值不相同, 则不会执行增加令牌数的操作. 这样设计的目的是, 如果你在不同机器中启动多个增加令牌数的程序, 而且这些程序启动时间不同, 那么其中一个程序将会起到增加令牌数的效果, 而另外的程序不会新增令牌数. 当增加令牌数的这个程序意外关闭, 将会有新的增加令牌的程序起作用. 这个实现的思想类似于乐观锁.具体代码如下:

// AddToken 用来添加某个key的令牌
func (acc *Accessor) AddToken(key string, keyIdx int, keyAdd int, keyLimit int) (int, error) {
    /* KEYS[1] 表示特定key,这个key是当前的令牌
     * KEYS[2] 表示特定key的idx
     * ARGV[1] 表示修改的key的增加的值
     * ARGV[2] 表示修改的key的最大值
     * ARGV[3] 表示修改的key的idx的序号
     */
    // 实现思路, 先判断这个key当前的序号与修改调用的序号是否一致,如果一致, 则进行修改,否则返回当前的序号
    keyAddScript :=
        `--[[测试显示, 通过call, 可以将error返回给客户端, 即使没有使用return]]--
        local curIdx = redis.call("INCR", KEYS[2])
        if (curIdx ~= (ARGV[3]+1))
        then
            curIdx = redis.call("DECR", KEYS[2])
            return curIdx
        end
        local curNum = redis.call("INCRBY", KEYS[1], ARGV[1])
        local maxNum = tonumber(ARGV[2])
        if (curNum > maxNum)
        then
            redis.call("SET", KEYS[1], ARGV[2])
        end
        return curIdx
        `
    keyAddCmd := redis.NewScript(keyAddScript)
    res, err := keyAddCmd.Run(acc.client, []string{key, getKeyIdx(key)},
        keyAdd, keyLimit, keyIdx).Result()
    if err != nil && err != redis.Nil {
        return 0, err
    }

    if idx, ok := res.(int64); ok {
        return int(idx), nil
    }

    return 0, errors.New("res should be integer")
}

完整的代码请参考如下地址:

https://github.com/ss-torres/ratelimiter.git

在这个git地址中, 如果想调用db_test.go中的测试, 可以参考如下命令:

go test ratelimiter/db -args "localhost:6379" "" "hello"

如果有什么好的建议, 或者有什么问题, 欢迎提出

原文地址:https://www.cnblogs.com/albizzia/p/10821176.html

时间: 2024-07-31 10:11:01

redis实现的简单令牌桶的相关文章

令牌桶过滤器(TBF)

令牌桶过滤器(TBF)是一个简单的队列规定,只允许不超过事先设定的速率到来的数据包通过,但可能允许短暂突发流量超过设定值. TBF很精确,对于网络和处理器的影响都比较小.如果对一个网卡限速,它应该成为第一选择. TBF的实现在于一个缓冲器(桶),不断地被一些叫做"令牌"的虚拟数据以特定速率填充(token rate).同最重要的参数就是它的大小,也就是它能够存储令牌的数量. 每个到来的令牌从数据队列中收集一个数据包,然后从桶中被删除.这个算法关联到两个流上----令牌流和数据流.于是由

理解流量监管和整形的关键算法—令牌桶

理解流量监管和整形的关键算法-令牌桶 无论是流量监管还是流量整形都提到一个超额流量的问题,而前面已经描述了监管和整形对超额流量的处理方式不同,监管丢弃或者重标记,流量整形是缓存,通过加大延迟的方式发送平滑的数据流量,那么网络设备怎么去确定这个超额流量,难道链路的带宽为512K,而此时用户以每秒768KB/s发送数据,使用768-512就256KB,难道超额的流量就是256KB吗?不是的,这样做是一种错误的理解,要确定用户的超额流量必须使用如下两种算法中的一种来确定,一种叫漏桶算法(leaky b

令牌桶-流量控制

作为后台服务,通常有一个处理极限PPS(packets per second),如果请求超过了这个处理能力,可能会出现“雪崩效应”,因此后台服务需要有过载保护机制. 1.有个简单的算法可以实现流量控制功能:设置一个单位时间(如1s, 1min)内的最大访问量,并维护一个单位时间里的计数器. 当访问请求到达时,先判断单位控制时间是否已经超时,如果已经超时,重置计数器为0; 否则,将计数器加1,并判断计数器的值是否超过最大访问量设置,如超过,则拒绝访问. 伪代码如下: 1 long timeStam

限流算法之漏桶算法、令牌桶算法

昨天CodeReview的时候看到同时使用RateLimiter这个类用作QPS访问限制.学习一下这个类. RateLimiter是Guava的concurrent包下的一个用于限制访问频率的类. 1.限流 每个API接口都是有访问上限的,当访问频率或者并发量超过其承受范围时候,我们就必须考虑限流来保证接口的可用性或者降级可用性.即接口也需要安装上保险丝,以防止非预期的请求对系统压力过大而引起的系统瘫痪. 通常的策略就是拒绝多余的访问,或者让多余的访问排队等待服务,或者引流. 如果要准确的控制Q

coding++:Semaphore—RateLimiter-漏桶算法-令牌桶算法

java中对于生产者消费者模型,或者小米手机营销 1分钟卖多少台手机等都存在限流的思想在里面. 关于限流 目前存在两大类,从线程个数(jdk1.5 Semaphore)和RateLimiter速率(guava) Semaphore:从线程个数限流 RateLimiter:从速率限流  目前常见的算法是漏桶算法和令牌算法 令牌桶算法.相比漏桶算法而言区别在于,令牌桶是会去匀速的生成令牌,拿到令牌才能够进行处理,类似于匀速往桶里放令牌 漏桶算法是:生产者消费者模型,生产者往木桶里生产数据,消费者按照

coding++:高并发解决方案限流技术-使用RateLimiter实现令牌桶限流-Demo

RateLimiter是guava提供的基于令牌桶算法的实现类,可以非常简单的完成限流特技,并且根据系统的实际情况来调整生成token的速率. 通常可应用于抢购限流防止冲垮系统:限制某接口.服务单位时间内的访问量,譬如一些第三方服务会对用户访问量进行限制:限制网速,单位时间内只允许上传下载多少字节等. guava的maven依赖 <dependency> <groupId>com.google.guava</groupId> <artifactId>guav

高并发学习之使用RateLimiter实现令牌桶限流

RateLimiter是guava提供的基于令牌桶算法的实现类,可以非常简单的完成限流特技,并且根据系统的实际情况来调整生成token的速率.通常可应用于抢购限流防止冲垮系统:限制某接口.服务单位时间内的访问量,譬如一些第三方服务会对用户访问量进行限制:限制网速,单位时间内只允许上传下载多少字节等. guava的maven依赖 <dependency> <groupId>com.google.guava</groupId> <artifactId>guava

Redis安装及简单测试

题目链接:11645 - Bits 题意:给定一个数字n,要求0-n的二进制形式下,连续11的个数. 思路:和 UVA 11038 这题类似,枚举中间,然后处理两边的情况. 不过本题最大的答案会超过longlong,要用高精度,不过借鉴http://www.cnblogs.com/TO-Asia/p/3214706.html这个人的方法,直接用两个数字来保存一个数字,这样能保存到2个longlong的长度,就足够存放这题的答案了. 代码: #include <stdio.h> #include

Redis入门很简单之七【使用Jedis实现客户端Sharding】

Redis入门很简单之七[使用Jedis实现客户端Sharding] 博客分类: NoSQL/Redis/MongoDB redisjedisspringsharding分片 <一>. 背景介绍: 1. sharding机制:即通常所说的"分片",允许数据存放在不同的物理机器上,  以适应数据量过大的场景,克服单台机器内存或者磁盘空间的限制.而这种"离散式"地存放,对客户端来说是透明的,对客户端来讲,完全看不到这种差别. 2. 常见的内存缓存中间件,比如