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

Lua限流器的3种写法

                  学而不思则罔,思而不学则殆

引言

上篇文章讲解了Lua脚本,事务和Pipline之间的使用方式和性能差距,本篇文章将聚焦Lua脚本,我将用三种写法来展现如何实现一个Redis限流器

固定窗口限流

固定窗口限流也是最简单的限流算法,它将时间划分为固定大小的窗口,每个窗口内允许的请求次数是固定的,也是最好理解的lua脚本

-- 获取限流的键名,通常为一个唯一标识,如用户 ID、IP 等
local key = KEYS[1]
-- 获取该窗口内允许的最大请求次数
local limit = tonumber(ARGV[1])
-- 获取当前窗口的时间长度(秒)
local window = tonumber(ARGV[2])-- 获取当前键对应的请求次数
local current = tonumber(redis.call('get', key) or "0")-- 如果当前请求次数超过了限制,返回 0 表示限流
if current + 1 > limit thenreturn 0
else-- 否则请求次数加 1local count = redis.call('incr', key)-- 如果是第一次请求,设置过期时间,保证窗口的有效性if count == 1 thenredis.call('expire', key, window)end-- 返回 1 表示允许请求return 1
end

本质是一个时间长度也就是窗口中我只允许固定次数通过,所以由此我们可以延伸出滑动窗口

滑动窗口限流

滑动窗口限流算法在固定窗口的基础上进行了改进,它将时间窗口划分为更小的时间片,每个时间片记录该时间段内的请求次数,通过滑动窗口的方式统计一定时间内的总请求次数

-- 获取限流的键名
local key = KEYS[1]
-- 获取该窗口内允许的最大请求次数
local limit = tonumber(ARGV[1])
-- 获取当前时间戳(毫秒)
local now = tonumber(ARGV[2])
-- 获取滑动窗口的时间长度(毫秒)
local window = tonumber(ARGV[3])-- 移除窗口外的请求记录
redis.call('zremrangebyscore', key, 0, now - window)
-- 获取当前窗口内的请求数量
local current = tonumber(redis.call('zcard', key))-- 如果当前请求次数超过了限制,返回 0 表示限流
if current + 1 > limit thenreturn 0
else-- 否则将当前请求的时间戳添加到有序集合中redis.call('zadd', key, now, now)-- 返回 1 表示允许请求return 1
end

原本的维度是一个窗口,而现在是将窗口放大成时间单位,然后用颗粒度更小的窗口去获取次数,最后统计这个大窗口的流量不变就行

令牌桶限流

令牌桶算法可以说是一种比较常用的限流算法,它以固定的速率向桶中添加令牌,每个请求需要从桶中获取一个或多个令牌才能被处理

-- 获取令牌桶的键名
local key = KEYS[1]
-- 获取桶的容量
local capacity = tonumber(ARGV[1])
-- 获取令牌生成的速率(每秒生成的令牌数)
local rate = tonumber(ARGV[2])
-- 获取当前时间戳(秒)
local now = tonumber(ARGV[3])
-- 获取每个请求需要的令牌数
local tokens_needed = tonumber(ARGV[4])-- 获取上次更新的时间和剩余的令牌数
local last_update = tonumber(redis.call('hget', key, 'last_update') or now)
local tokens = tonumber(redis.call('hget', key, 'tokens') or capacity)-- 计算从上次更新到现在应该生成的令牌数
local new_tokens = math.min(capacity, tokens + (now - last_update) * rate)-- 如果剩余的令牌数足够,处理请求
if new_tokens >= tokens_needed then-- 更新剩余的令牌数local remaining_tokens = new_tokens - tokens_needed-- 更新上次更新的时间和剩余的令牌数redis.call('hset', key, 'last_update', now)redis.call('hset', key, 'tokens', remaining_tokens)-- 返回 1 表示允许请求return 1
else-- 否则返回 0 表示限流return 0
end

实战Java

一般我们的Lua脚本都是放在配置文件中管理,无论是阅读性还是维护性都是最优解

  1. 例如把上面的任一脚本创建于rate_limiter.lua文件中(自定义名称)

  2. 配合Jedis我们常用的redis命令框架

    import redis.clients.jedis.Jedis;
    import java.io.BufferedReader;
    import java.io.FileReader;
    import java.io.IOException;
    import java.util.Arrays;
    import java.util.List;public class RedisRateLimiterFromConfigJedis {public static void main(String[] args) {// 连接 RedisJedis jedis = new Jedis("localhost", 6379);// 限流键名String key = "rate_limit:user:1";// 窗口内允许的最大请求次数int limit = 10;// 窗口时间长度(秒)int window = 60;// 读取 Lua 脚本文件String script = readScriptFromFile("rate_limiter.lua");// 键名参数列表List<String> keys = Arrays.asList(key);// 其他参数列表List<String> argsList = Arrays.asList(String.valueOf(limit), String.valueOf(window));// 执行 Lua 脚本Object result = jedis.eval(script, keys, argsList);if (result instanceof Long && (Long) result == 1) {System.out.println("允许请求");} else {System.out.println("请求被限流");}// 关闭连接jedis.close();}private static String readScriptFromFile(String filePath) {StringBuilder script = new StringBuilder();try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {String line;while ((line = reader.readLine()) != null) {script.append(line).append("\n");}} catch (IOException e) {e.printStackTrace();}return script.toString();}
    }

    其中端口和文件名可以自定义

相关文章:

Lua限流器的3种写法

学而不思则罔&#xff0c;思而不学则殆 引言 上篇文章讲解了Lua脚本&#xff0c;事务和Pipline之间的使用方式和性能差距&#xff0c;本篇文章将聚焦Lua脚本&#xff0c;我将用三种写法来展现如何实现一个Redis限流器 固定窗口限流 固定窗口限流也是最简单的限流算法&#x…...

hive的几种复杂数据类型

Hive的几种复杂数据类型 Hive 提供了几种复杂数据类型&#xff0c;能够支持更灵活和多样的数据存储。这些复杂数据类型对于处理嵌套数据或不规则数据特别有用。主要包括以下几种&#xff1a; 文章目录 Hive的几种复杂数据类型1. 数组&#xff08;ARRAY&#xff09;2. 结构体&a…...

序列化/反序列化与TCP通信协议

深入理解序列化/反序列化与TCP通信协议 一、序列化与反序列化 1.1 基本概念 序列化&#xff08;Serialization&#xff09;: 将数据结构或对象状态转换为可存储/传输格式的过程反序列化&#xff08;Deserialization&#xff09;: 将序列化后的数据恢复为原始数据结构的过程 …...

Ollama 本地部署 体验 deepseek

下载安装ollama,选择模型 进行部署 # 管理员命令行 执行 ollama run deepseek-r1:70b浏览器访问http://ip:11434/ 返回 Ollama is runninghttp://ip:11434/v1/models 返回当前部署的模型数据 下载安装CherryStudio&#xff0c;本地对话UI 客户端 在设置中 修改API地址&#x…...

Linux探秘坊-------4.进度条小程序

1.缓冲区 #include <stdio.h> int main() {printf("hello bite!");sleep(2);return 0; }执行此代码后&#xff0c;会 先停顿两秒&#xff0c;再打印出hello bite&#xff0c;但是明明打印在sleep前面&#xff0c;为什么会后打印呢&#xff1f; 因为&#xff…...

postgreSQL16.6源码安装

1.获取源码 从PostgreSQL: File Browser获取tar.bz2或者tar.gz源码 2.解压 tar xf postgresql-version.tar.bz2 roothwz-VMware-Virtual-Platform:/usr/local# tar xf postgresql-16.6.tar.bz2 roothwz-VMware-Virtual-Platform:/usr/local# ll 总计 24324 drwxr-xr-x 12 ro…...

树莓派上 基于Opencv 实现人脸检测与人脸识别

一&#xff0c;需求 基于树莓派4b&#xff0c;usb1080p摄像头&#xff0c;实现人脸检测与人脸识别。尝试了海陵科的模组和百度的sdk。海陵科的模组无法录入人脸&#xff0c;浪费了100多块钱。百度的sdk 在树莓派上也无法录入人脸&#xff0c;官方解决不了。最后只能用opencv自…...

vscode怎么更新github代码

vscode怎么更新github代码 打开终端&#xff1a; 在 VS Code 中&#xff0c;使用快捷键 Ctrl (Mac 上是 Cmd) 打开终端。 导航到项目目录&#xff1a; 确保你当前所在的终端目录是你的项目目录。如果不是&#xff0c;可以使用 cd 命令导航到项目目录&#xff0c;例如&#xf…...

Golang Web单体项目目录结构最佳实践

在Golang 开发Web 项目的过程中&#xff0c;如何组织目录结构是一项至关重要的任务。合理的目录结构不仅能提高代码的可维护性&#xff0c;还能为团队协作提供清晰的代码规范。 为什么要设计合理的目录结构&#xff1f; 在 Golang 项目中&#xff0c;代码的组织方式会影响开发…...

Zookeeper 作注册中心 和nacos 和eruka 有什么差异 ?基于什么理论选择?

目录 三者的差异 1. 设计定位与功能特性 2. 服务注册与发现机制 3. 可用性与容错性 4. 性能 选择依据 1. 业务场景 2. 可用性和一致性需求 3. 性能需求 三者的差异 1. 设计定位与功能特性 Zookeeper 设计定位:最初是为分布式应用提供协调服务的,本身并不是专门作为…...

mac下dify+deepseek部署,实现私人知识库

目前deepseek 十分火爆&#xff0c;本地部署实现私有知识库&#xff0c;帮助自己日常工作&#xff0c;上一篇使用工具cherry studio可以做到私人知识库。今天学习了一下&#xff0c;使用Dify链接deepseek&#xff0c;实现私人知识库&#xff0c;也非常不错&#xff0c;这里分享…...

CSS 实现下拉菜单效果实例解析

1. 引言 在 Web 开发过程中&#xff0c;下拉菜单是一种常见且十分实用的交互组件。很多前端教程都提供过简单的下拉菜单示例&#xff0c;本文将以一个简洁的实例为出发点&#xff0c;从 HTML 结构、CSS 样式以及整体交互逻辑三个层面进行详细解析&#xff0c;帮助大家理解纯 C…...

C# 比较两个List集合内容是否相同

在 C# 中&#xff0c;要比较两个 List<T> 集合的内容是否相同&#xff0c;可以通过以下几种方法&#xff1a; 一、非自定义类的元素比较 1. 使用 SequenceEqual 方法&#xff08;顺序和内容都相等&#xff09; 顺序和内容都相等&#xff1a;使用 SequenceEqual。 usin…...

x64、aarch64、arm与RISC-V64:详解四种处理器架构

x64、aarch64、arm与RISC-V64:详解四种处理器架构 x64架构aarch64架构ARM架构RISC-V64架构总结与展望在计算机科学领域,处理器架构是构建计算机系统的基石,它决定了计算机如何执行指令、管理内存和处理数据。x64、aarch64、arm与RISC-V64是当前主流的四种处理器架构,它们在…...

vscode卡住---回退版本解决

一、回退 今日本人打开vscode&#xff0c;发现慢到起飞&#xff0c;最终卡到起飞 删除缓存&#xff1a; C:\Users\python\AppData\Roaming\Code 重启发现回到刚安装时的界面&#xff0c;但是插件还在。启动速度快了&#xff0c;但是一打开terminal就卡。 关闭vscode,重装&…...

Java使用aspose实现pdf转word

Java使用aspose实现pdf转word 一、下载aspose-pdf-21.6.jar包【下载地址】&#xff0c;存放目录结构如图&#xff1b;配置pom.xml。 <!--pdf to word--> <dependency><groupId>com.aspose</groupId><artifactId>aspose-pdf</artifactId>…...

国产编辑器EverEdit - 迷你查找

1 迷你查找 1.1 应用场景 某些场景下&#xff0c;用户不希望调出复杂的查找对话框&#xff0c;此时可以使用迷你查找窗口。 1.2 使用方法 选择主菜单查找 -> 迷你查找&#xff0c;或使用快捷键Ctrl Alt F&#xff0c;会在右上角弹出迷你查找窗口&#xff0c;如下图所示…...

嵌入式音视频开发(一)ffmpeg框架及内核解析

系列文章目录 嵌入式音视频开发&#xff08;零&#xff09;移植ffmpeg及推流测试 嵌入式音视频开发&#xff08;一&#xff09;ffmpeg框架及内核解析 文章目录 系列文章目录前言一、ffmpeg的内核1.1 框架解析1.2 内核解析1.3 FFmpeg内部数据流1.3.1 典型的解码流程1.3.2 典型的…...

javaEE-11.javaScript入门

目录 一.什么是javaScript 二.快速实现 三.JS引入方式 1.行内引入: 2.内部引入: 3.外部引入: 四.基础语法 1.变量 变量命名规则: 2.数据类型 3.运算符 五.JS对象 1.数组 创建数组: 2.操作数组 3.函数 函数注意事项: 函数参数: 4.对象 1.使用字面量 创建对象:…...

畅游Diffusion数字人(16):由音乐驱动跳舞视频生成

畅游Diffusion数字人(0):专栏文章导航 前言:从Pose到跳舞视频生成的工作非常多,但是还没有直接从音乐驱动生成的工作。最近字节跳动提出了MuseDance,无需复杂的动作引导输入(如姿势或深度序列),从而使不同专业水平的用户都能轻松进行灵活且富有创意的视频生成。 目录 贡…...

OnlyOffice docker 运行(详细)

下载镜像 推荐使用 GitHub Action 方式下载&#xff1a; Action 地址&#xff1a;https://github.com/Shixuebin/DockerTarBuilder 教程地址&#xff1a;https://www.bilibili.com/video/BV1EZ421M7mL/ docker 镜像安装 docker load -i xxx.tar镜像运行 docker run -i -t -…...

DeepSeek 助力 Vue 开发:打造丝滑的步骤条

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 Deep…...

领略算法真谛:差分

嘿&#xff0c;各位技术潮人&#xff01;好久不见甚是想念。生活就像一场奇妙冒险&#xff0c;而编程就是那把超酷的万能钥匙。此刻&#xff0c;阳光洒在键盘上&#xff0c;灵感在指尖跳跃&#xff0c;让我们抛开一切束缚&#xff0c;给平淡日子加点料&#xff0c;注入满满的pa…...

【Python深入浅出】Python3中os模块:开启系统交互的万能钥匙

目录 一、引言&#xff1a;os 模块初印象二、os 模块基础操作2.1 文件与目录操作2.1.1 创建操作2.1.2 读取操作2.1.3 删除操作2.1.4 信息获取 2.2 系统信息获取与环境变量管理2.2.1 系统信息获取2.2.2 环境变量管理 2.3 进程管理与工作目录操作2.3.1 进程管理2.3.2 工作目录操作…...

【图片转换PDF】多个文件夹里图片逐个批量转换成多个pdf软件,子文件夹单独合并转换,子文件夹单独批量转换,基于Py的解决方案

建筑设计公司在项目执行过程中&#xff0c;会产生大量的设计图纸、效果图、实景照片等图片资料。这些资料按照项目名称、阶段、专业等维度存放在多个文件夹和子文件夹中。 操作需求&#xff1a;为了方便内部管理和向客户交付完整的设计方案&#xff0c;公司需要将每个项目文件…...

在Linux上如何让ollama在GPU上运行模型

之前一直在 Mac 上使用 ollama 所以没注意&#xff0c;最近在 Ubuntu 上运行发现一直在 CPU 上跑。我一开始以为是超显存了&#xff0c;因为 Mac 上如果超内存的话&#xff0c;那么就只用 CPU&#xff0c;但是我发现 Llama3.2 3B 只占用 3GB&#xff0c;这远没有超。看了一下命…...

程序诗篇里的灵动笔触:指针绘就数据的梦幻蓝图<8>

大家好啊&#xff0c;我是小象٩(๑ω๑)۶ 我的博客&#xff1a;Xiao Xiangζั͡ޓއއ 很高兴见到大家&#xff0c;希望能够和大家一起交流学习&#xff0c;共同进步。 今天我们复习前面学习的指针知识 目录 关于指针数组和数组指针的区别指针数组&#xff08;Array of Poi…...

快速集成DeepSeek到项目

DeepSeek API-KEY 获取 登录DeekSeek 官网&#xff0c;进入API 开放平台 2. 创建API-KEY 复制API-KEY进行保存&#xff0c;后期API调用使用 项目中集成DeepSeek 这里只展示部分核心代码&#xff0c;具体请查看源码orange-ai-deepseek-biz-starter Slf4j AllArgsConstructo…...

DeepSeek做赛车游戏

赛车模型 2D生成图片 任意AI图片软件SD&#xff0c;MJ 图片生成3D模型 车身 车轮 场景 Rodin,Tripo和Meshy 询问deepSeek如何开发 拷贝代码 将汽车运行代码拖到汽车上 再让AI写个摄像头跟随代码 再去提问deepseek控制轮胎和一些处理细节...

未来替代手机的产品,而非手机的本身

替代手机的产品包括以下几种&#xff1a; 可穿戴设备&#xff1a;智能手表、智能眼镜等可穿戴设备可以提供类似手机的功能&#xff0c;如通话、信息推送、浏览网页等。 虚拟现实&#xff08;VR&#xff09;技术&#xff1a;通过佩戴VR头显&#xff0c;用户可以进行语音通话、发…...