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

2024-06-19 00:12

本文主要是介绍使用 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 的高性能和丰富功能使其成为实现分布式限流的理想选择。

这篇关于使用 Redis + Lua 实现分布式限流的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1073454

相关文章

关于集合与数组转换实现方法

《关于集合与数组转换实现方法》:本文主要介绍关于集合与数组转换实现方法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、Arrays.asList()1.1、方法作用1.2、内部实现1.3、修改元素的影响1.4、注意事项2、list.toArray()2.1、方

使用Python实现可恢复式多线程下载器

《使用Python实现可恢复式多线程下载器》在数字时代,大文件下载已成为日常操作,本文将手把手教你用Python打造专业级下载器,实现断点续传,多线程加速,速度限制等功能,感兴趣的小伙伴可以了解下... 目录一、智能续传:从崩溃边缘抢救进度二、多线程加速:榨干网络带宽三、速度控制:做网络的好邻居四、终端交互

Python中注释使用方法举例详解

《Python中注释使用方法举例详解》在Python编程语言中注释是必不可少的一部分,它有助于提高代码的可读性和维护性,:本文主要介绍Python中注释使用方法的相关资料,需要的朋友可以参考下... 目录一、前言二、什么是注释?示例:三、单行注释语法:以 China编程# 开头,后面的内容为注释内容示例:示例:四

java实现docker镜像上传到harbor仓库的方式

《java实现docker镜像上传到harbor仓库的方式》:本文主要介绍java实现docker镜像上传到harbor仓库的方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录1. 前 言2. 编写工具类2.1 引入依赖包2.2 使用当前服务器的docker环境推送镜像2.2

Redis出现中文乱码的问题及解决

《Redis出现中文乱码的问题及解决》:本文主要介绍Redis出现中文乱码的问题及解决,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1. 问题的产生2China编程. 问题的解决redihttp://www.chinasem.cns数据进制问题的解决中文乱码问题解决总结

C++20管道运算符的实现示例

《C++20管道运算符的实现示例》本文简要介绍C++20管道运算符的使用与实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录标准库的管道运算符使用自己实现类似的管道运算符我们不打算介绍太多,因为它实际属于c++20最为重要的

Java easyExcel实现导入多sheet的Excel

《JavaeasyExcel实现导入多sheet的Excel》这篇文章主要为大家详细介绍了如何使用JavaeasyExcel实现导入多sheet的Excel,文中的示例代码讲解详细,感兴趣的小伙伴可... 目录1.官网2.Excel样式3.代码1.官网easyExcel官网2.Excel样式3.代码

Go语言数据库编程GORM 的基本使用详解

《Go语言数据库编程GORM的基本使用详解》GORM是Go语言流行的ORM框架,封装database/sql,支持自动迁移、关联、事务等,提供CRUD、条件查询、钩子函数、日志等功能,简化数据库操作... 目录一、安装与初始化1. 安装 GORM 及数据库驱动2. 建立数据库连接二、定义模型结构体三、自动迁

python实现对数据公钥加密与私钥解密

《python实现对数据公钥加密与私钥解密》这篇文章主要为大家详细介绍了如何使用python实现对数据公钥加密与私钥解密,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录公钥私钥的生成使用公钥加密使用私钥解密公钥私钥的生成这一部分,使用python生成公钥与私钥,然后保存在两个文

ModelMapper基本使用和常见场景示例详解

《ModelMapper基本使用和常见场景示例详解》ModelMapper是Java对象映射库,支持自动映射、自定义规则、集合转换及高级配置(如匹配策略、转换器),可集成SpringBoot,减少样板... 目录1. 添加依赖2. 基本用法示例:简单对象映射3. 自定义映射规则4. 集合映射5. 高级配置匹