当前位置: 首页 > news >正文

用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 中的应用令牌桶算法是一种常用的限流算法&#xff0c;适用于控制请求的频率&#xff0c;防止系统过载。结合 Redis 使用可以实现高效的分布式限流。 一.、引入依赖首先&#xff0c;需要在 pom.xml 文件中引入 spring-boot-starter-data-re…...

DCGAN - 深度卷积生成对抗网络:基于卷积神经网络的GAN

深度卷积生成对抗网络&#xff08;DCGAN&#xff0c;Deep Convolutional Generative Adversarial Network&#xff09;是生成对抗网络&#xff08;GAN&#xff09;的一种扩展&#xff0c;它通过使用卷积神经网络&#xff08;CNN&#xff09;来实现生成器和判别器的构建。与标准…...

51c~SLAM~合集1

我自己的原文哦~ https://blog.51cto.com/whaosoft/12327374 #GSLAM 自动驾驶相关~~~ 一个通用的SLAM架构和基准 GSLAM&#xff1a;A General SLAM Framework and Benchmark 开源代码&#xff1a;https://github.com/zdzhaoyong/GSLAM SLAM技术最近取得了许多成功&am…...

优化使用 Flask 构建视频转 GIF 工具

优化使用 Flask 构建视频转 GIF 工具 优化后的项目概述 在优化后的版本中&#xff0c;我们将实现以下功能&#xff1a; 可设置每个 GIF 的帧率和大小&#xff1a;用户可以选择 GIF 的帧率和输出大小。改进的用户界面&#xff1a;使用更现代的设计使界面更美观、整洁。自定义…...

spring cloud如何实现负载均衡

在Spring Cloud中&#xff0c;实际上并没有直接支持lb:\\这样的URL前缀来自动解析为负载均衡的服务地址。lb:\\这样的表示可能是在某些特定框架、文档或示例中自定义的&#xff0c;但它并不是Spring Cloud官方API或规范的一部分。 Spring Cloud实现负载均衡的方式通常依赖于服…...

leetcode19-删除链表的第n结点

leetcode 19 思路 要删除倒数第n个元素&#xff0c;那么就要找到倒数第n1个元素&#xff0c;那么我们需要两个指针来记录&#xff0c;首先快指针需要先走n1步&#xff0c;然后快慢指针一起进行移动&#xff0c;直到快指针为null的时候&#xff0c;此时慢指针恰好走到倒数第n…...

软件测试—— 接口测试(HTTP和HTTPS)

软件测试—— 接口测试&#xff08;HTTP和HTTPS&#xff09; HTTP请求方法GET特点使用场景URL结构URL组成部分URL编码总结 POST特点使用场景请求结构示例 请求标头和响应标头请求标头&#xff08;Request Headers&#xff09;示例请求标头 响应标头&#xff08;Response Header…...

3.1 Go函数调用过程

在 Go 语言中&#xff0c;函数调用的核心机制依赖于内存的栈区分配和指针操作&#xff0c;理解这一原理有助于掌握函数的执行过程。 1. 内存结构概述 在 Go 程序编译成可执行文件并启动后&#xff0c;操作系统会为其分配进程内存&#xff0c;进程内存主要分为以下区域&#x…...

TDengine 做 Apache SuperSet 数据源

‌Apache Superset‌ 是一个现代的企业级商业智能&#xff08;BI&#xff09;Web 应用程序&#xff0c;主要用于数据探索和可视化。它由 Apache 软件基金会支持&#xff0c;是一个开源项目&#xff0c;它拥有活跃的社区和丰富的生态系统。Apache Superset 提供了直观的用户界面…...

08_游戏启动逻辑

1.GameRoot.cs 控制 服务层Svc.cs 和业务层Sys.cs 的初始化 创建脚本GameRoot.cs&#xff08;游戏入口 已进入就初始化各个系统&#xff09; 创建资源加载服务.cs Res 将服务层Svc设置成单例类所以需要挂载在GameRoot身上&#xff0c;这样就可以通过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中&#xff0c;如果后端NFS存储的IP地址发生了变化&#xff0c;您需要更新与之相关的Peristent Volume(PV)或Persistent Volume Claim(PVC)以及StorageClass中关于NFS服务器IP的配置信息&#xff0c;确保K8S集群内的Pod能够正确连接到新的NFS存储位置。解决方案如下…...

模拟飞行入坑(五) P3D 多通道视角配置 viewgroup

背景&#xff1a; P3D进行多个屏幕显示的时候&#xff0c;如果使用英伟达自带的屏幕融合成一个屏&#xff0c;或者使用P3D单独拉伸窗口&#xff0c;会使得P3D的画面被整体拉伸&#xff0c;又或者,当使用Multichannel进行多个设备联动时&#xff0c;视角同步组合需要配置&#…...

【springboot集成knife4j】

SpringBoot集成knife4j Knife4j是为Java MVC框架集成Swagger生成API文档的一套增强解决方案&#xff0c;它基于Swagger原有的基础上进行了一些改进和增强&#xff0c;提供了更简洁的UI界面&#xff0c;同时支持更多的自用化配置。下面是在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 集群管理器。 官…...

如何选择一款助贷获客系统?

做助贷的销售们&#xff0c;一天打几百个电话&#xff0c;跑各种新媒体平台评论区偷流量&#xff0c;每天忙得昏天黑地&#xff0c;也没有多少客户。没有精准数据&#xff0c;助贷销售着急&#xff0c;公司也着急&#xff0c;每天让员工加班找客户&#xff0c;但是巧妇难为无米…...

GDB相比IDE有什么优点

GDB(GNU Debugger)相比于集成开发环境(IDE)具有一些独特的优点,主要体现在其灵活性、可定制性和低级控制能力。具体来说,GDB有以下几个优点: 1. 轻量级且无依赖 GDB是一个命令行工具,不依赖于任何复杂的图形界面或大型库,这使得它非常适合在资源受限的环境中使用,比…...

介绍用于机器学习的 Fashion-MNIST 数据集

介绍用于机器学习的 Fashion-MNIST 数据集 为什么要研究数据集&#xff1f; 让我们首先思考一下为什么要花时间研究数据集的问题。数据是深度学习的主要成分&#xff0c;虽然作为神经网络程序员的任务是让我们的神经网络从我们的数据中学习&#xff0c;但我们仍然有责任了解我…...

【GitHub】登录时的2FA验证

一、如何进行2FA认证 1.在你的浏览器中下载 Authenticator身份验证插件 2.使用身份验证器添加凭证 2.1 使用身份验证器扫描验证二维码 选择扫描二维码...

CSDN年度回顾:技术征途上的坚实步伐

嘿&#xff0c;时光过得可真快呀&#xff0c;就像那匹跑得飞快的白马&#xff0c;嗖的一下&#xff0c;2024 年的日历就这么悄无声息地翻到了最后一页。这会儿我回头看看在 CSDN 上度过的这一年&#xff0c;心里那叫一个感慨万千&#xff0c;满满的都是喜悦&#xff0c;就像心里…...

k8s从入门到放弃之Ingress七层负载

k8s从入门到放弃之Ingress七层负载 在Kubernetes&#xff08;简称K8s&#xff09;中&#xff0c;Ingress是一个API对象&#xff0c;它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress&#xff0c;你可…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八

现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet&#xff0c;点击确认后如下提示 最终上报fail 解决方法 内核升级导致&#xff0c;需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...

iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版​分享

平时用 iPhone 的时候&#xff0c;难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵&#xff0c;或者买了二手 iPhone 却被原来的 iCloud 账号锁住&#xff0c;这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...

【大模型RAG】Docker 一键部署 Milvus 完整攻略

本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装&#xff1b;只需暴露 19530&#xff08;gRPC&#xff09;与 9091&#xff08;HTTP/WebUI&#xff09;两个端口&#xff0c;即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...

条件运算符

C中的三目运算符&#xff08;也称条件运算符&#xff0c;英文&#xff1a;ternary operator&#xff09;是一种简洁的条件选择语句&#xff0c;语法如下&#xff1a; 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true&#xff0c;则整个表达式的结果为“表达式1”…...

服务器硬防的应用场景都有哪些?

服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式&#xff0c;避免服务器受到各种恶意攻击和网络威胁&#xff0c;那么&#xff0c;服务器硬防通常都会应用在哪些场景当中呢&#xff1f; 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...

大语言模型如何处理长文本?常用文本分割技术详解

为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...

学校招生小程序源码介绍

基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码&#xff0c;专为学校招生场景量身打造&#xff0c;功能实用且操作便捷。 从技术架构来看&#xff0c;ThinkPHP提供稳定可靠的后台服务&#xff0c;FastAdmin加速开发流程&#xff0c;UniApp则保障小程序在多端有良好的兼…...

P3 QT项目----记事本(3.8)

3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...

【HTTP三个基础问题】

面试官您好&#xff01;HTTP是超文本传输协议&#xff0c;是互联网上客户端和服务器之间传输超文本数据&#xff08;比如文字、图片、音频、视频等&#xff09;的核心协议&#xff0c;当前互联网应用最广泛的版本是HTTP1.1&#xff0c;它基于经典的C/S模型&#xff0c;也就是客…...