java redis令牌桶_redis实现的简单令牌桶

java redis令牌桶_redis实现的简单令牌桶这里给出的令牌桶是以 redis 单节点或者集群为中间件 不过 这里的实现比较简单 主要提供两个函数 一个用于消费令牌 一个用于添加令牌 这里 消费令牌和添加令牌都是通过 lua 来保证原子性 消费令牌的代码如下 FetchToken 用来获取某个 key 的一个令牌 func acc Accessor FetchToken keystring bool error

这里给出的令牌桶是以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 res == 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”)

}

假设现在有多个节点,例如有20个节点,我希望每次都有3个节点作为添加令牌桶的节点,那么这个怎么实现呢?

/ 作用: 判断这个节点是否用于新增键令牌(以下称为: 加令牌节点),

* 从而实现每个redis(或者redis集群)总是有N个节点(例如2个或者3个)用于添加令牌操作

* 我们可能采用多种方式获取所有的key, 然后向对应的key增加令牌, 例如

* (1) 通过数据库获取所有键值

* (2) 通过遍历redis获取所有键值

* (3) 直接读取配置文件

* (4) 通过远程调用设置

* 判断方式通过如下实现:

* (1) 实现这个判断需要在redis中使用一个键值存储信息, 这里使用”{}”的键值, 这个键存储所有的加令牌节点

* 1) 这个键使用类型为list

* 2) 这个list中存储的值为是否可用标示+”:”+节点标识符(可用时, 为”1:节点标识符”)

* (2) 程序启动时, 监听这个键的修改操作

* 1) “加令牌节点”监听对这个键的包括LREM,LSET和PUSH类型的消息

* 2) 非”加令牌节点”监听对这个键的包括LREM类型的消息

* (3) 然后, 读取redis中键名为”{}”的键的值, 然后判断当前”加令牌节点”的个数, 如果这个个数小于配置值,

* 则修改redis中这个键的值,将自己设为”加令牌节点”, 否则, 不做处理

* (4) 对于每个设置为”加令牌节点”的应用, 会在这个list中排在后面的M个节点(例如2个或者3个)建立tcp连接,

* 然后通过ping和pong消息, 来判断这个节点是否可以连接.

* (5) 如果A节点发现B节点不可连接(假设每秒发送一条消息,经过20次没有发送成功), 向redis发送一条修改请求,

* 请求中先查看B节点是否可用,如果B节点当前显示可用, 那么修改”{}”的值,设置1+”:”+节点标识符为

* 0+”:”+节点标识符(B节点), 如果显示B节点已经不可用, 则继续进行tcp通信, (如果B节点已经不存在,

* 则断开与这个节点的tcp连接,按照当前逻辑,这个应该不会出现)

* (6) 节点继续等待若干时间(例如15s), 在期间查看这个节点是否已经被重置正常,如果恢复正常,则继续进行tcp通信,

* 查看是否存在问题,如果没有恢复正常,从这个list中清除这个节点

* (7) 那些非”加令牌节点”接收到清楚的消息之后, 会申请自己成为”加令牌节点”,会检测当前”加令牌节点”的个数,

* 如果条件满足, 则将这个节点信息插入, 让这个节点成为”加令牌节点”, 然后, 每个当前”加令牌节点”

* 读取所有的”加令牌节点”, 重新更新与哪些节点建立tcp连接.

* (8) 当前标识符为Ip:port*/

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

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

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

go test ratelimiter/db -args “localhost:6379” “” “hello”

当前这个实现,没有充分利用redis的提供的功能,按照我的观点来看,使用redis的subscribe和publish的实现可能更加简单,之后可能会提供使用subscribe和publish的实现,然后再进行必要的详细测试和benchmark。另外,使用zookeeper作为中间的协调中间件的实现可能更加简单。

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

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/177012.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月26日 下午8:31
下一篇 2026年3月26日 下午8:31


相关推荐

  • 微信公众号开发教程(一) 验证接入[通俗易懂]

    作者:陈惠,叩丁狼教育高级讲师。原创文章,转载请注明出处。微信公众号开发教程(一)验证接入本篇文章主要介绍了微信公众号开发接入详细流程,希望对刚接触公众号开发的同学有所帮助,有兴趣的同学可多多关注叩丁狼公众号,后续会更新不同的公众号小案例。公众号的分类我们平常在微信应用上会看到有很多的公众号,但是各自并不一样,公众号也分很多种类型,不过最常见的就是服务号和订阅号了。下面我们来看一下他们的区别:1、…

    2022年4月12日
    455
  • 扣子手机版的使用教程

    扣子手机版的使用教程

    2026年3月12日
    2
  • 三字经 全文解读(三字经全文朗读儿童版解读)

    前言:《三字经》自南宋王应麟(字伯厚)先生所创作以来,已有七百多年历史,内容大都采用韵文,每三字一句,四句一组,像一首诗一样,背诵起来,如唱儿歌,三字经是学习中华传统文化不可多得的的儿童启蒙读物,共一千多字,可谓家喻户晓,脍炙人口。内容包括了中国传统的教育、历史、天文、地理、伦理和道德以及一些民间传说,广泛生动而又言简意赅。用来教育子女琅琅上口十分有趣,又能启迪心智,时人觉得本书内容很好,纷纷翻印

    2022年4月10日
    85
  • 网络推广100种方法有哪些_100种宣传方式

    网络推广100种方法有哪些_100种宣传方式一米软件发现网上很多人都在找网络推广100种方法,但 其实网站推广并不是方法越多越好,而是找到适合自己的方法为宜,下面一米软件就来给大家介绍下一些常用网站推广的方法。1、搜索引擎营销搜索引擎营销,这种方法一般是通过自建官网然后针对官网进行优化与更新,使得网站在搜索引擎中有一个好的排名。也有通过做付费推广,使得网站的某个关键词在搜索引擎中有个好的排名。2、自媒…

    2022年4月20日
    193
  • Ags 9.3 文档逐步上线

    Ags 9.3 文档逐步上线

    2021年7月27日
    51

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号