Redis限流IP的那些事儿,怎么用Redis搞定IP限流问题
- 问答
- 2026-01-16 21:30:48
- 3
(来源:博客园《Redis实现IP限流实战》)Redis限流这事儿,说白了就是防止某些IP地址在短时间内太“热情”,比如一秒内请求几百次,这谁受得了?服务器会被搞垮的,用Redis来做这个特别合适,因为它速度快,能应对高并发,而且数据结构灵活。
最常见的办法是用一个叫“滑动时间窗口”的招数,这名字听着玄乎,其实道理很简单,想象一下,我们把时间切成一个个小格子,比如一秒一个格子,现在我要限制某个IP一分钟内最多只能访问60次,那我就看看从当前时刻往前推的一分钟里,这个IP已经占了多少个格子。(来源:开源项目redis-cell的算法原理)但精确切分一秒秒算有点麻烦,所以实践中常用一种更平滑的方式。

一个经典做法是使用Redis的列表(List)数据结构,具体这么干:每当一个IP发来请求,我就做两件事,第一,用当前的时间戳(比如精确到毫秒)作为值,塞进这个IP对应的列表的最左边,第二,把列表里所有超过一分钟以前的时间戳都从右边扔掉,我只要查一下现在这个列表的长度,也就是还剩下多少个时间戳,如果长度已经超过了60,那就说明这一分钟内它已经请求超过60次了,立马拒绝它;如果没超过,就放行。(来源:CSDN《基于Redis的分布式限流方案设计》)
这个方法挺直观的,但有个小缺点,如果某个IP在短时间内突然爆发,比如一秒内猛冲60次,那么虽然那一分钟内的总次数没超,但那一秒的压力也很大,为了解决这个问题,我们还可以加个“令牌桶”的机制。(来源:网络文章《Redis + Lua实现限流器》)令牌桶的想法也挺好玩,想象一个桶,里面放着一些令牌,系统以固定的速率往桶里扔新令牌(比如每秒扔10个),但桶有容量上限,比如最多存100个令牌,每次IP要访问,就必须从桶里拿走一个令牌,如果桶是空的,那就对不起,请稍后再来,这样既能限制平均速率,又能允许一定程度的突发流量(因为桶里可能存了一些令牌)。

用Redis实现令牌桶也不难,我们可以用一个键来存当前桶里剩余的令牌数,用另一个键来记录上次补充令牌的时间,每次请求来时,先计算一下从上一次到现在应该补进多少新令牌(但不能超过桶容量),更新令牌数,然后判断如果令牌数大于零,就减一然后放行;否则就拒绝。(来源:个人技术博客《手写Redis限流组件》)为了确保整个计算过程是原子性的,不会因为并发而出错,我们得用Redis的Lua脚本把这几步操作打包成一个整体来执行,Redis执行Lua脚本时是不会被其他命令打断的。
除了控制访问频率,有时候我们还想防“抖动”,也就是防止特别密集的连续攻击,这时候可以结合使用Redis的过期时间(expire)功能,当发现某个IP在非常短的时间(比如10秒)内连续失败多次(比如密码错误5次),我们可以直接把这个IP关进“小黑屋”(设置一个键,并设置过期时间为10分钟),在这10分钟内,这个IP的任何登录尝试都直接拒绝,这就是一种更严格的限流或者说是封禁。(来源:知乎问答《如何用Redis实现简单的防暴力破解》)
实际用的时候,选择哪种方法得看具体场景,如果只是想让服务器压力平均一点,用滑动时间窗口或者令牌桶就挺好,如果是安全考虑,要防恶意攻击,那可能就需要更果断的封禁策略,Redis给我们提供了这些好用的工具,让我们能用简单的命令组合出强大的限流功能,关键就是利用好它的速度、数据结构和原子操作。
本文由颜泰平于2026-01-16发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:https://haoid.cn/wenda/82019.html
