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

使用 Redis + Lua 实现分布式限流

在线工具站
  • 推荐一个程序员在线工具站:程序员常用工具(http://cxytools.com),有时间戳、JSON格式化、文本对比、HASH生成、UUID生成等常用工具,效率加倍嘎嘎好用。
程序员资料站
  • 推荐一个程序员编程资料站:程序员的成长之路(http://cxyroad.com),收录了一些列的技术教程、各大面试专题,还有常用开发工具的教程。
小报童专栏精选Top100
  • 推荐一个小报童专栏导航站:小报童精选Top100(http://xbt100.top),收录了生财有术项目精选、AI海外赚钱、纯银的产品分析等专栏,陆续会收录更多的专栏,欢迎体验~

分布式系统中,限流是保证系统稳定性和可用性的重要措施之一。通过限流,可以避免流量突增对系统带来的冲击,保证系统的高可用性。Redis 作为一个高性能的内存数据库,不仅可以用来缓存数据,还可以用来实现分布式限流。而通过结合 Lua 脚本,可以更高效地实现复杂的限流逻辑。本文将介绍如何使用 Redis 和 Lua 脚本实现分布式限流。

为什么选择 Redis + Lua 实现限流

Redis 是一个高性能的键值存储系统,具有以下特点使其成为实现分布式限流的理想选择:

  1. 高性能:Redis 以内存存储为基础,读写速度极快,能够处理高并发请求。
  2. 丰富的数据结构:Redis 提供了多种数据结构,如字符串、列表、集合、哈希等,便于灵活实现各种限流策略。
  3. 持久化支持:Redis 支持数据持久化,可以在重启后恢复数据。
  4. Lua 脚本支持:Redis 内置 Lua 脚本引擎,可以在一次请求中执行一系列操作,保证原子性,避免并发问题。

限流策略介绍

常见的限流策略包括以下几种:

  1. 固定窗口限流:在固定时间窗口内限制请求数量,超过限制则拒绝请求。
  2. 滑动窗口限流:相对于固定窗口,滑动窗口限流更加精细化,可以更平滑地限制请求。
  3. 令牌桶算法:以固定速率生成令牌,请求需要消耗令牌,没有令牌则拒绝请求。
  4. 漏桶算法:以固定速率处理请求,超过速率的请求则排队等待。

本文将以固定窗口限流和令牌桶算法为例,介绍如何使用 Redis + Lua 实现分布式限流。

实现固定窗口限流

固定窗口限流是一种简单的限流策略,在固定时间窗口内限制请求数量。可以通过 Redis 的 INCREXPIRE 命令配合 Lua 脚本实现。

Lua 脚本实现

local key = KEYS[1]
local limit = tonumber(ARGV[1])
local current = tonumber(redis.call('GET', key) or "0")if current + 1 > limit thenreturn 0
elseredis.call("INCRBY", key, 1)redis.call("EXPIRE", key, ARGV[2])return 1
end

使用示例

假设限流策略为每分钟最多允许 100 次请求:

import redisclient = redis.StrictRedis(host='localhost', port=6379, db=0)# Lua script for fixed window rate limiting
lua_script = """
local key = KEYS[1]
local limit = tonumber(ARGV[1])
local current = tonumber(redis.call('GET', key) or "0")if current + 1 > limit thenreturn 0
elseredis.call("INCRBY", key, 1)redis.call("EXPIRE", key, ARGV[2])return 1
end
"""rate_limit_script = client.register_script(lua_script)key = "rate_limit:fixed_window"
limit = 100
expiry = 60  # time window in secondsresult = rate_limit_script(keys=[key], args=[limit, expiry])
if result == 1:print("Request allowed")
else:print("Request rate-limited")

实现令牌桶算法限流

令牌桶算法是另一种常见的限流策略,可以较好地平衡突发流量和长期流量。通过定期向桶中添加令牌,请求时消耗令牌,没有令牌则拒绝请求。

Lua 脚本实现

local key = KEYS[1]
local rate = tonumber(ARGV[1])
local burst = tonumber(ARGV[2])
local now = tonumber(ARGV[3])
local requested = tonumber(ARGV[4])local fill_time = burst / rate
local ttl = math.floor(fill_time * 2)
local last_tokens = tonumber(redis.call("GET", key .. ":tokens"))
if last_tokens == nil thenlast_tokens = burst
end
local last_refreshed = tonumber(redis.call("GET", key .. ":timestamp"))
if last_refreshed == nil thenlast_refreshed = 0
endlocal delta = math.max(0, now - last_refreshed)
local filled_tokens = math.min(burst, last_tokens + (delta * rate))
local allowed = filled_tokens >= requested
local new_tokens = filled_tokens
if allowed thennew_tokens = filled_tokens - requested
endredis.call("SETEX", key .. ":tokens", ttl, new_tokens)
redis.call("SETEX", key .. ":timestamp", ttl, now)return allowed and 1 or 0

使用示例

假设限流策略为每秒生成 10 个令牌,桶容量为 20:

import redis
import timeclient = redis.StrictRedis(host='localhost', port=6379, db=0)# Lua script for token bucket rate limiting
lua_script = """
local key = KEYS[1]
local rate = tonumber(ARGV[1])
local burst = tonumber(ARGV[2])
local now = tonumber(ARGV[3])
local requested = tonumber(ARGV[4])local fill_time = burst / rate
local ttl = math.floor(fill_time * 2)
local last_tokens = tonumber(redis.call("GET", key .. ":tokens"))
if last_tokens == nil thenlast_tokens = burst
end
local last_refreshed = tonumber(redis.call("GET", key .. ":timestamp"))
if last_refreshed == nil thenlast_refreshed = 0
endlocal delta = math.max(0, now - last_refreshed)
local filled_tokens = math.min(burst, last_tokens + (delta * rate))
local allowed = filled_tokens >= requested
local new_tokens = filled_tokens
if allowed thennew_tokens = filled_tokens - requested
endredis.call("SETEX", key .. ":tokens", ttl, new_tokens)
redis.call("SETEX", key .. ":timestamp", ttl, now)return allowed and 1 or 0
"""token_bucket_script = client.register_script(lua_script)key = "rate_limit:token_bucket"
rate = 10  # tokens per second
burst = 20  # bucket capacity
now = int(time.time())
requested = 1result = token_bucket_script(keys=[key], args=[rate, burst, now, requested])
if result == 1:print("Request allowed")
else:print("Request rate-limited")

优化和扩展

监控与报警

为了及时发现限流策略的异常情况,可以结合监控工具如 Prometheus 和 Grafana,对 Redis 的限流情况进行监控和报警。通过定期统计限流命中次数和请求数量,可以分析限流效果,并及时调整限流策略。

动态调整限流策略

在实际应用中,可能需要根据流量情况动态调整限流策略。可以结合 Redis 的 Pub/Sub 功能,通过发布和订阅消息来实时调整限流参数。

集群环境中的限流

在 Redis 集群环境中,可以将限流脚本分发到各个节点,确保限流逻辑在各个节点上执行。可以使用 Redis 的主从复制和分片机制,实现全局的分布式限流。

总结

通过结合 Redis 和 Lua 脚本,可以高效实现多种限流策略,如固定窗口限流和令牌桶算法。Redis 的高性能和丰富功能使其成为实现分布式限流的理想选择。

相关文章:

  • 如何修改jupyter notebook 默认把文件夹
  • 会声会影2024永久破解和谐版下载 包含激活码序列号
  • 深入理解RunLoop
  • 决策树算法详细介绍原理和实现
  • HarmonyOS角落里的知识—Stage模型应用程序
  • python基础1.2----爬虫基础
  • ninja构建笔记
  • 2024.6.17-21学习日报
  • Go微服务: redis分布式锁
  • css中content属性你了解多少?
  • 【Python Cookbook】S02E12 字符串的连接及合并 ‘ ‘.join()
  • 解决uniapp h5 本地代理实现跨域访问及如何配置开发环境
  • C语言笔记25 •顺序表介绍•
  • Ubuntu乌班图安装VIM文本编辑器工具
  • k8s解决java服务下载超时问题
  • ----------
  • 【附node操作实例】redis简明入门系列—字符串类型
  • 2018天猫双11|这就是阿里云!不止有新技术,更有温暖的社会力量
  • angular学习第一篇-----环境搭建
  • - C#编程大幅提高OUTLOOK的邮件搜索能力!
  • Date型的使用
  • JavaScript 奇技淫巧
  • Redis中的lru算法实现
  • vue 配置sass、scss全局变量
  • yii2中session跨域名的问题
  • 记一次删除Git记录中的大文件的过程
  • 力扣(LeetCode)357
  • 使用agvtool更改app version/build
  • 小程序开发之路(一)
  • 一加3T解锁OEM、刷入TWRP、第三方ROM以及ROOT
  • 一起参Ember.js讨论、问答社区。
  • 与 ConTeXt MkIV 官方文档的接驳
  • 06-01 点餐小程序前台界面搭建
  • 好程序员web前端教程分享CSS不同元素margin的计算 ...
  • ​TypeScript都不会用,也敢说会前端?
  • # Swust 12th acm 邀请赛# [ A ] A+B problem [题解]
  • #前后端分离# 头条发布系统
  • (2)空速传感器
  • (vue)el-cascader级联选择器按勾选的顺序传值,摆脱层级约束
  • (备忘)Java Map 遍历
  • (二)原生js案例之数码时钟计时
  • (分布式缓存)Redis分片集群
  • (分享)一个图片添加水印的小demo的页面,可自定义样式
  • (附源码)ssm学生管理系统 毕业设计 141543
  • (附源码)流浪动物保护平台的设计与实现 毕业设计 161154
  • (含react-draggable库以及相关BUG如何解决)固定在左上方某盒子内(如按钮)添加可拖动功能,使用react hook语法实现
  • (深度全面解析)ChatGPT的重大更新给创业者带来了哪些红利机会
  • (十一)图像的罗伯特梯度锐化
  • (四)Linux Shell编程——输入输出重定向
  • (一)Spring Cloud 直击微服务作用、架构应用、hystrix降级
  • (原創) 如何將struct塞進vector? (C/C++) (STL)
  • (原創) 是否该学PetShop将Model和BLL分开? (.NET) (N-Tier) (PetShop) (OO)
  • (转载)CentOS查看系统信息|CentOS查看命令
  • ***详解账号泄露:全球约1亿用户已泄露
  • .[hudsonL@cock.li].mkp勒索病毒数据怎么处理|数据解密恢复