分布式限流——Redis + Lua脚本实现令牌桶算法

 

主要思路概括如下:

  1. 定义数据结构

    • 使用Redis存储令牌桶的状态,包括当前令牌数(KEYS[1])和上一次令牌填充的时间戳(KEYS[1]:last)。
  2. 计算新增令牌

    • 获取当前系统时间与上次令牌填充时间的时间差,并基于令牌生成速率计算在这段时间内应新增的令牌数。
    • 确保新增令牌数不超过桶的总容量。
  3. 更新令牌数

    • 将令牌桶内的令牌数增加至新的值,确保不超过桶的最大容量。
  4. 判断是否满足请求

    • 如果更新后的令牌数足以满足本次请求消耗的令牌数,则扣除相应数量的令牌并返回1,表示请求通过。
    • 否则,返回0,表示请求被限流。
  5. 原子性操作

    • 利用Redis Lua脚本提供的原子性执行能力,确保在多客户端并发访问时,令牌桶状态的读取、更新等操作是线程安全的。
  6. 实时性

    • 脚本设计考虑到随着时间推移动态补充令牌,但实际应用中可能需要更精确的定时任务或者使用Redis的过期时间特性来定期补充令牌。

整个令牌桶算法在Redis和Lua脚本中的实现旨在提供一种简单且高效的手段来进行流量控制,既可以应对突发流量,又能保持总体速率稳定可控。

-- KEYS[1] 是令牌桶的键名
-- ARGV[1] 是令牌生成速率(每秒产生的令牌数量)
-- ARGV[2] 是桶容量(最大令牌存储量)
-- ARGV[3] 是请求需要消耗的令牌数量
-- ARGV[4] 是当前时间戳(毫秒级,由客户端提供)-- 获取当前桶内令牌数
local tokenCount = tonumber(redis.call('GET', KEYS[1]))-- 如果桶内令牌数为空或不存在,则初始化为桶容量
if tokenCount == nil thentokenCount = tonumber(ARGV[2])
end-- 计算从上次填充到现在应该产生的令牌数
local currentTime = tonumber(ARGV[4])
local lastRefillTimestamp = tonumber(redis.call('GET', KEYS[1] .. ':last'))
if lastRefillTimestamp == nil thenlastRefillTimestamp = currentTime
end
local newTokens = math.min((currentTime - lastRefillTimestamp) * tonumber(ARGV[1]), tonumber(ARGV[2]) - tokenCount)-- 更新令牌数(注意这里假设时间粒度较小,不考虑精确到毫秒的令牌生成)
tokenCount = math.min(tokenCount + newTokens, tonumber(ARGV[2]))-- 设置最后填充令牌的时间为当前时间
redis.call('SET', KEYS[1] .. ':last', currentTime)-- 如果新加入的令牌仍然不够消耗,则请求被限流
if tokenCount < tonumber(ARGV[3]) thenreturn 0  -- 表示限流
else
-- 消耗相应数量的令牌tokenCount = tokenCount - tonumber(ARGV[3])redis.call('SET', KEYS[1], tokenCount)return 1  -- 表示通过
end

在上面的Lua脚本示例中,上次填充令牌的时间戳是通过另一个Redis键来保存的。在脚本中,这个键是通过在令牌桶的主键后面加上:last_refill来构成的,如 KEYS[1] .. ":last_refill"

这意味着对于一个令牌桶的键 bucket_key,它的最后一次填充令牌的时间戳会被保存在 bucket_key:last_refill 这个额外的键中。

在脚本执行过程中,当需要获取上次填充令牌的时间戳时,通过调用 redis.call('GET', KEYS[1] .. ":last_refill") 来获取。如果没有获取到值(即第一次运行或者之前没有设置过),则会将其默认设置为当前时间戳。

同样,在完成令牌填充的逻辑后,也会更新这个时间戳键的值,通过 redis.call('SET', KEYS[1] .. ":last_refill", currentTime) 实现,其中 currentTime 是客户端提供的当前时间戳。

 

这个Lua脚本简化了令牌桶算法的实现,实际应用中可能还需要考虑更多细节,例如如何精确地按照时间间隔补充令牌、如何处理多个请求同时竞争令牌等情况。客户端在调用这个Lua脚本时,应当传递正确的参数,包括令牌桶的键名以及令牌相关的配置信息。此外,在高并发场景下,为了避免多个客户端同时修改令牌数,Lua脚本的执行必须是原子性的,这正是Redis.eval/evalsha命令所提供的功能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/824379.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

信息系统项目管理师0053:设计和实施(4信息系统管理—4.1管理方法—4.1.3设计和实施)

点击查看专栏目录 文章目录 4.1.3设计和实施1.设计方法2.架构模式4.1.3设计和实施 开展信息系统设计和实施,首先需要将业务需求转换为信息系统架构,信息系统架构为将组织业务战略转换为信息系统的计划提供了蓝图。信息系统是支持组织中信息流动和处理的所有基础,包括硬件、软…

前端请求发送成功,后端收到null

1、dishId为64&#xff0c;有数据 2、但是后端调试接不到数据&#xff0c;为null 3、形参部分缺少RequestBody接收JSON数据&#xff0c;加上即可

数据赋能(58)——要求:数据赋能实施部门能力

“要求&#xff1a;数据赋能实施部门能力”是作为标准的参考内容编写的。 在实施数据赋能中&#xff0c;数据赋能实施部门的能力体现在多个方面&#xff0c;关键能力如下图所示。 在实施数据赋能的过程中&#xff0c;数据赋能实施部门应具备的关键能力如下。 理性思维与逻辑分…

网络协议——IS-IS协议详解

1. IS-IS是什么 IS-IS是一种基于链路状态并使用最短路径优先算法进行路由计算的一种IGP协议。IS-IS属于内部网关协议&#xff0c;用于自治系统内部。IS-IS是一种链路状态协议&#xff0c;使用最短路径优先算法进行路由计算。 2. 应用场景&#xff08;园区网和骨干网&#xff0…

Xamarin.Android中“ADB0020: Android ABI 不匹配。你正将应用支持的“armeabi-v7a;arm64-v8a”异常处理

这里写自定义目录标题 1、问题2、解决 1、问题 在Xamarin.Android中出现ADB0020: Android ABI 不匹配。你正将应用支持的“armeabi-v7a;arm64-v8a”ABI 部署到 ABI“x86_64;x86”的不兼容设备。应创建匹配其中一个应用 ABI 的仿真程序&#xff0c;或将“x86_64”添加到应用生成…

手撸词法分析器(C/C++)

手撸词法分析器&#xff08;C/C&#xff09; 一.背景二.什么是词法分析器&#xff1f;三.代码四.思考 一.背景 这学期开设了编译原理&#xff0c;要求写个基本的词法分析器。所以博主就自己写了一份代码&#xff0c;也比较简单基础。 二.什么是词法分析器&#xff1f; 简单来…

Postman之接口测试

接口测试的必要条件 &#xff1a;请求方式、请求协议、请求地址、请求头、请求参数 常用请求方式 &#xff1a;Get请求&#xff08;get请求一般是获取数据&#xff09;、Post请求&#xff08;post请求一般是提交数据&#xff09; 传参格式 &#xff1a;表单提交、请求体提交 注…

什么是神经网络和机器学习?【云驻共创】

什么是神经网络和机器学习&#xff1f; 一.背景 在当今数字化浪潮中&#xff0c;神经网络和机器学习已成为科技领域的中流砥柱。它们作为人工智能的支柱&#xff0c;推动了自动化、智能化和数据驱动决策的进步。然而&#xff0c;对于初学者和专业人士来说&#xff0c;理解神经…

设计模式-构建者模式

作者持续关注 WPS二次开发专题系列&#xff0c;持续为大家带来更多有价值的WPS二次开发技术细节&#xff0c;如果能够帮助到您&#xff0c;请帮忙来个一键三连&#xff0c;更多问题请联系我&#xff08;QQ:250325397&#xff09; 目录 定义 特点 使用场景 优缺点 (1) 优点 …

Jmeter BeanShell调用Java方法加密

1、添加BeanShell前置处理器 由于请求接口时&#xff0c;会传加密参数。加密过程会在请求之前完成&#xff0c;所以需要使用前置处理器中beanshell preprocessor 2、编写BeanShell脚本 ①定义一个beashell变量&#xff1a;phoneNum&#xff0c;在Beanshell中可以直接调用Jmete…

EFK环境搭建(基于K8S环境部署)

目录 一.环境信息二.安装nfs供应商三.安装elasticsearch四.安装kibana组件五.安装fluentd 一.环境信息 1.服务器及k8s版本 IP地址主机名称角色版本192.168.40.180master1master节点1.27192.168.40.181node1node1节点1.27192.168.40.182node2node2节点1.27 2.部署组件版本 序…

go语言并发实战——日志收集系统(三) 利用sarama包连接KafKa实现消息的生产与消费

环境的搭建 Kafka以及相关组件的下载 我们要实现今天的内容&#xff0c;不可避免的要进行对开发环境的配置&#xff0c;Kafka环境的配置比较繁琐&#xff0c;需要配置JDK,Scala,ZoopKeeper和Kafka&#xff0c;这里我们不做赘述&#xff0c;如果大家不知道如何配置环境&#x…

元宇宙-虚拟世界的安全风险如何应对

元宇宙&#xff08;Metaverse&#xff09;是一个虚拟时空间的集合&#xff0c;由一系列的增强现实&#xff08;AR&#xff09;、虚拟现实&#xff08;VR&#xff09;和互联网&#xff08;Internet&#xff09;所组成。这个虚拟时空间是一个持续存在的、由众多虚拟世界互相连接而…

redis与etcd的对比

1.redis是一种高级的key&#xff1a;value存储系统&#xff0c;其中value支持五种数据类型&#xff1a; 1.1 字符串&#xff08;strings&#xff09; 1.2 字符串列表&#xff08;lists&#xff09; 1.3 字符串集合&#xff08;sets&#xff09; 1.4 有序字符串集合&#xff08;…

STM32 HAL库F103系列之ADC实验(1)

ADC工作原理&#xff1a; 1、输入通道&#xff1a; 2、转换序列&#xff1a; A/D转换被组织为两组&#xff1a;规则组&#xff08;常规转换组&#xff09;和注入组&#xff08;注入转换组&#xff09; 规则组最多可以有16个转换&#xff0c;注入组最多有4个转换 规则组和注入…

Hudi-IDEA编程

项目 一、HudiSparkKafka&#xff08;Scala&#xff09; 配置详见【1.Scala配置】 依赖详见【1.HudiSparkKafka依赖】 1-1 构建SparkSession对象 def main(args: Array[String]): Unit {//1.构建SparkSession对象val spark: SparkSession SparkUtils.createSparkSession(…

中科亿海微-CL1656功能验证开发板

I. 引言 A. 研究背景与意义 CL1656是一款精度高、功耗低、成本低的5V单片低功耗运放&#xff0c;由核心互联公司研发制造&#xff0c;CL1656 是一个 16-bit、快速、低功耗逐次逼近型 ADC&#xff0c;吞吐速率高达 250 kSPS&#xff0c;并且内置低噪声、宽 带宽采样保持放大器。…

HarmonyOS开发实例:【分布式新闻客户端】

介绍 本篇Codelab基于栅格布局、设备管理和多端协同&#xff0c;实现一次开发&#xff0c;多端部署的分布式新闻客户端页面。主要包含以下功能&#xff1a; 展示新闻列表以及左右滑动切换新闻Tab。点击新闻展示新闻详情页。点击新闻详情页底部的分享按钮&#xff0c;发现周边…

Elasticsearch:如何将 MongoDB 数据引入 Elastic Cloud

作者&#xff1a;Hemendra Singh Lodhi Elastic Cloud 是由 Elastic 提供的基于云的托管服务。Elastic Cloud 允许客户在亚马逊网络服务 (AWS)、谷歌云平台 (GCP) 和微软 Azure 上部署、管理和扩展他们的 Elasticsearch 集群。 MongoDB 是一种流行的 NoSQL 文档导向数据库&am…

web安全学习笔记(10)

记一下第十四节课的内容。 一、MySQL学习 数据库基本结构&#xff1a;库——表——列——值 在本地打开navicat&#xff0c;连接数据库&#xff0c;新建一个liuyan库、liuyan库下新建一个member表&#xff1a; 在表里随意添加一些数据&#xff1a; 下面我们学习MySQL查询。新…