用java配合redis 在springboot上实现令牌桶算法
令牌桶算法配合 Redis 在 Java 中的应用令牌桶算法是一种常用的限流算法,适用于控制请求的频率,防止系统过载。结合 Redis 使用可以实现高效的分布式限流。
一.、引入依赖首先,需要在 pom.xml 文件中引入 spring-boot-starter-data-redis 依赖,这个依赖提供了与 Redis 交互的客户端和工具类。
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>
</dependencies>
二.、配置 Redis 连接 (我随便用了application.properties ,也可以用 application.yml)
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=
spring.redis.jedis.pool.max-active=8
spring.redis.jedis.pool.max-wait=-1
spring.redis.jedis.pool.max-idle=8
spring.redis.jedis.pool.min-idle=0
spring.redis.timeout=2000
三.、实现令牌桶算法
使用 Lua 脚本和 Redis 操作来实现令牌桶算法。在resources创建一个 Lua 脚本文件 request_rate_limiter.lua
-- 获取到限流资源令牌数的key和响应时间戳的key
local tokens_key = KEYS[1]
local timestamp_key = KEYS[2]
-- 分别获取填充速率、令牌桶容量、当前时间戳、消耗令牌数
local rate = tonumber(ARGV[1])
local capacity = tonumber(ARGV[2])
local now = tonumber(ARGV[3])
local requested = tonumber(ARGV[4])
-- 计算出失效时间,大概是令牌桶填满时间的两倍
local fill_time = capacity / rate
local ttl = math.floor(fill_time * 2)
-- 获取到最近一次的剩余令牌数,如果不存在说明令牌桶是满的
local last_tokens = tonumber(redis.call("get", tokens_key))
if last_tokens == nil thenlast_tokens = capacity
end
-- 上次消耗令牌的时间戳,不存在视为0
local last_refreshed = tonumber(redis.call("get", timestamp_key))
if last_refreshed == nil thenlast_refreshed = 0
end
-- 计算出间隔时间
local delta = math.max(0, now - last_refreshed)
-- 剩余令牌数量 = “令牌桶容量” 和 “最后令牌数+(填充速率*时间间隔)”之间的最小值
local filled_tokens = math.min(capacity, last_tokens + (delta * rate))
-- 如果剩余令牌数量大于等于消耗令牌的数量则流量通过,否则不通过
local allowed = filled_tokens >= requested
local new_tokens = filled_tokens
local allowed_num = 0
if allowed thennew_tokens = filled_tokens - requestedallowed_num = 1
end
-- 更新令牌桶状态
if ttl > 0 thenredis.call("setex", tokens_key, ttl, new_tokens)redis.call("setex", timestamp_key, ttl, now)
end
return allowed_num
四、创建一个 TokenBucket 类,使用 StringRedisTemplate 执行 Lua 脚本。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;import javax.xml.crypto.Data;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;@Component
public class TokenBucket {private static final String TOKEN_BUCKET_KEY_PREFIX = "rate_limiter:";private static final String LUA_SCRIPT_PATH = "request_rate_limiter.lua";@Autowiredprivate StringRedisTemplate stringRedisTemplate;public boolean tryAccess(String key, int limitCount, int refillRate) {String luaScript = loadLuaScript();// 使用加载的Lua脚本创建一个RedisScript 对象。 DefaultRedisScript 是Spring Data Redis提供的一个类,用于封装Lua脚本。RedisScript<Long> redisScript = new DefaultRedisScript<>(luaScript, Long.class);// TOKEN_BUCKET_KEY_PREFIX + key + ":tokens":用于存储令牌桶中的令牌数量。•// TOKEN_BUCKET_KEY_PREFIX + key + ":timestamp":用于存储上一次令牌桶被填充的时间戳。List<String> keys = Arrays.asList(TOKEN_BUCKET_KEY_PREFIX + key + ":tokens", TOKEN_BUCKET_KEY_PREFIX + key + ":timestamp");//执行Lua脚本/*** 使用 StringRedisTemplate 执行Lua脚本。execute方法的参数包括:* • redisScript :Lua脚本对象。* • keys:Redis键列表。* • String.valueOf(refillRate):令牌桶的填充速率。* • String.valueOf(limitCount):令牌桶的最大容量。* • String.valueOf(System.currentTimeMillis() / 1000):当前时间戳(秒级)。* • "1" :请求的令牌数量(这里假设每次请求需要1个令牌)。*/Long result = stringRedisTemplate.execute(redisScript, //Lua脚本对象。keys, //Redis键列表String.valueOf(refillRate), //令牌桶的填充速率String.valueOf(limitCount), //令牌桶的最大容量String.valueOf(System.currentTimeMillis() / 1000), //当前时间戳(秒级)"1" //请求的令牌数量(这里假设每次请求需要1个令牌)。);return result != null && result == 1;}private String loadLuaScript() {InputStreamReader reader = null;try {//使用类加载器从指定路径 LUA_SCRIPT_PATH 获取资源流。我的lua文件在resources根目录下面//使用 InputStreamReader 将输入流 resourceStream 包装成一个字符流,并指定字符编码为UTF-8。这样可以确保读取的文件内容是正确的编码格式reader = new InputStreamReader(getClass().getClassLoader().getResourceAsStream(LUA_SCRIPT_PATH), StandardCharsets.UTF_8);/*创建一个Scanner对象,用于读取字符流。useDelimiter("\\A"):设置分隔符为文件的开始标记 \\A 。这意味着Scanner会将整个文件内容视为一个单一的字符串。next() :读取并返回整个文件内容*/return new java.util.Scanner(reader).useDelimiter("\\A").next();} catch (Exception e) {throw new RuntimeException("Failed to load Lua script", e);}}
}
五、测试
package com.lhx.testany.controller;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class RateLimiterController {@Autowiredprivate TokenBucket tokenBucket;@GetMapping("/test")public String testRateLimiter() {//为了测试,把最多设成2个令牌,每秒只生成1个令牌, 我快速在浏览器调用时,//生成的令牌,不足于被消耗,就会执行esle,这样就能做限流if (tokenBucket.tryAccess("api1", 2, 1)) {return "Request allowed";} else {return "Request denied due to rate limit";}}
}

相关文章:
用java配合redis 在springboot上实现令牌桶算法
令牌桶算法配合 Redis 在 Java 中的应用令牌桶算法是一种常用的限流算法,适用于控制请求的频率,防止系统过载。结合 Redis 使用可以实现高效的分布式限流。 一.、引入依赖首先,需要在 pom.xml 文件中引入 spring-boot-starter-data-re…...
DCGAN - 深度卷积生成对抗网络:基于卷积神经网络的GAN
深度卷积生成对抗网络(DCGAN,Deep Convolutional Generative Adversarial Network)是生成对抗网络(GAN)的一种扩展,它通过使用卷积神经网络(CNN)来实现生成器和判别器的构建。与标准…...
51c~SLAM~合集1
我自己的原文哦~ https://blog.51cto.com/whaosoft/12327374 #GSLAM 自动驾驶相关~~~ 一个通用的SLAM架构和基准 GSLAM:A General SLAM Framework and Benchmark 开源代码:https://github.com/zdzhaoyong/GSLAM SLAM技术最近取得了许多成功&am…...
优化使用 Flask 构建视频转 GIF 工具
优化使用 Flask 构建视频转 GIF 工具 优化后的项目概述 在优化后的版本中,我们将实现以下功能: 可设置每个 GIF 的帧率和大小:用户可以选择 GIF 的帧率和输出大小。改进的用户界面:使用更现代的设计使界面更美观、整洁。自定义…...
spring cloud如何实现负载均衡
在Spring Cloud中,实际上并没有直接支持lb:\\这样的URL前缀来自动解析为负载均衡的服务地址。lb:\\这样的表示可能是在某些特定框架、文档或示例中自定义的,但它并不是Spring Cloud官方API或规范的一部分。 Spring Cloud实现负载均衡的方式通常依赖于服…...
leetcode19-删除链表的第n结点
leetcode 19 思路 要删除倒数第n个元素,那么就要找到倒数第n1个元素,那么我们需要两个指针来记录,首先快指针需要先走n1步,然后快慢指针一起进行移动,直到快指针为null的时候,此时慢指针恰好走到倒数第n…...
软件测试—— 接口测试(HTTP和HTTPS)
软件测试—— 接口测试(HTTP和HTTPS) HTTP请求方法GET特点使用场景URL结构URL组成部分URL编码总结 POST特点使用场景请求结构示例 请求标头和响应标头请求标头(Request Headers)示例请求标头 响应标头(Response Header…...
3.1 Go函数调用过程
在 Go 语言中,函数调用的核心机制依赖于内存的栈区分配和指针操作,理解这一原理有助于掌握函数的执行过程。 1. 内存结构概述 在 Go 程序编译成可执行文件并启动后,操作系统会为其分配进程内存,进程内存主要分为以下区域&#x…...
TDengine 做 Apache SuperSet 数据源
Apache Superset 是一个现代的企业级商业智能(BI)Web 应用程序,主要用于数据探索和可视化。它由 Apache 软件基金会支持,是一个开源项目,它拥有活跃的社区和丰富的生态系统。Apache Superset 提供了直观的用户界面…...
08_游戏启动逻辑
1.GameRoot.cs 控制 服务层Svc.cs 和业务层Sys.cs 的初始化 创建脚本GameRoot.cs(游戏入口 已进入就初始化各个系统) 创建资源加载服务.cs Res 将服务层Svc设置成单例类所以需要挂载在GameRoot身上,这样就可以通过GameRoot来调各个服务 接…...
Ardupilot开源无人机之Geek SDK进展2024-2025
Ardupilot开源无人机之Geek SDK进展2024-2025 1. 源由2. 状态3. TODO3.1 【进行中】跟踪目标框3.2 【暂停】onnxruntime版本3.3 【完成】CUDA 11.8版本3.4 【完成】pytorch v2.5.1版本3.5 【未开始】Inference性能3.6 【未开始】特定目标集Training 4. Extra-Work4.1 【完成】C…...
在K8S中,如果后端NFS存储的IP发送变化如何解决?
在Kubernetes中,如果后端NFS存储的IP地址发生了变化,您需要更新与之相关的Peristent Volume(PV)或Persistent Volume Claim(PVC)以及StorageClass中关于NFS服务器IP的配置信息,确保K8S集群内的Pod能够正确连接到新的NFS存储位置。解决方案如下…...
模拟飞行入坑(五) P3D 多通道视角配置 viewgroup
背景: P3D进行多个屏幕显示的时候,如果使用英伟达自带的屏幕融合成一个屏,或者使用P3D单独拉伸窗口,会使得P3D的画面被整体拉伸,又或者,当使用Multichannel进行多个设备联动时,视角同步组合需要配置&#…...
【springboot集成knife4j】
SpringBoot集成knife4j Knife4j是为Java MVC框架集成Swagger生成API文档的一套增强解决方案,它基于Swagger原有的基础上进行了一些改进和增强,提供了更简洁的UI界面,同时支持更多的自用化配置。下面是在Spring Boot项目中集成Knife4j的基本步…...
GPUStack使用
1. 概述 官网:https://github.com/gpustack Open-source GPU cluster manager for running large language models(LLMs) https://github.com/gpustack/gpustack,Manage GPU clusters for running AI models GPUStack 是一个用于运行 AI 模型的开源 GPU 集群管理器。 官…...
如何选择一款助贷获客系统?
做助贷的销售们,一天打几百个电话,跑各种新媒体平台评论区偷流量,每天忙得昏天黑地,也没有多少客户。没有精准数据,助贷销售着急,公司也着急,每天让员工加班找客户,但是巧妇难为无米…...
GDB相比IDE有什么优点
GDB(GNU Debugger)相比于集成开发环境(IDE)具有一些独特的优点,主要体现在其灵活性、可定制性和低级控制能力。具体来说,GDB有以下几个优点: 1. 轻量级且无依赖 GDB是一个命令行工具,不依赖于任何复杂的图形界面或大型库,这使得它非常适合在资源受限的环境中使用,比…...
介绍用于机器学习的 Fashion-MNIST 数据集
介绍用于机器学习的 Fashion-MNIST 数据集 为什么要研究数据集? 让我们首先思考一下为什么要花时间研究数据集的问题。数据是深度学习的主要成分,虽然作为神经网络程序员的任务是让我们的神经网络从我们的数据中学习,但我们仍然有责任了解我…...
【GitHub】登录时的2FA验证
一、如何进行2FA认证 1.在你的浏览器中下载 Authenticator身份验证插件 2.使用身份验证器添加凭证 2.1 使用身份验证器扫描验证二维码 选择扫描二维码...
CSDN年度回顾:技术征途上的坚实步伐
嘿,时光过得可真快呀,就像那匹跑得飞快的白马,嗖的一下,2024 年的日历就这么悄无声息地翻到了最后一页。这会儿我回头看看在 CSDN 上度过的这一年,心里那叫一个感慨万千,满满的都是喜悦,就像心里…...
51c自动驾驶~合集58
我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留,CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制(CCA-Attention),…...
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...
Qt Widget类解析与代码注释
#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码,写上注释 当然可以!这段代码是 Qt …...
蓝桥杯 2024 15届国赛 A组 儿童节快乐
P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡,轻快的音乐在耳边持续回荡,小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下,六一来了。 今天是六一儿童节,小蓝老师为了让大家在节…...
Leetcode 3577. Count the Number of Computer Unlocking Permutations
Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接:3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯,要想要能够将所有的电脑解锁&#x…...
【机器视觉】单目测距——运动结构恢复
ps:图是随便找的,为了凑个封面 前言 在前面对光流法进行进一步改进,希望将2D光流推广至3D场景流时,发现2D转3D过程中存在尺度歧义问题,需要补全摄像头拍摄图像中缺失的深度信息,否则解空间不收敛…...
JAVA后端开发——多租户
数据隔离是多租户系统中的核心概念,确保一个租户(在这个系统中可能是一个公司或一个独立的客户)的数据对其他租户是不可见的。在 RuoYi 框架(您当前项目所使用的基础框架)中,这通常是通过在数据表中增加一个…...
面向无人机海岸带生态系统监测的语义分割基准数据集
描述:海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而,目前该领域仍面临一个挑战,即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...
LangFlow技术架构分析
🔧 LangFlow 的可视化技术栈 前端节点编辑器 底层框架:基于 (一个现代化的 React 节点绘图库) 功能: 拖拽式构建 LangGraph 状态机 实时连线定义节点依赖关系 可视化调试循环和分支逻辑 与 LangGraph 的深…...
