使用Redis实现限流

server/2024/12/23 19:54:13/

使用Redis实现限流的三种方式

请添加图片描述

目录

  1. 概述
  2. 基于计数器的固定窗口限流
    • 实现原理
    • 适用场景
    • 实现步骤
    • 代码实现
    • 缺点
  3. 基于滑动窗口的限流
    • 实现原理
    • 适用场景
    • 实现步骤
    • 代码实现
    • 优点
    • 缺点
  4. 基于令牌桶算法的限流
    • 实现原理
    • 适用场景
    • 实现步骤
    • Lua脚本实现
    • Java实现
    • 优点
    • 缺点
  5. 总结

概述

在分布式系统中,限流是保护服务的重要手段之一。通过限流,可以防止接口被恶意刷请求或突发流量压垮,从而保证系统的稳定性。Redis是一个高性能的键值存储工具,因其高效的读写性能和丰富的数据结构,被广泛用于限流场景。本文将介绍三种使用Redis实现限流的方式,并通过代码示例说明其实现原理和应用场景。

限流算法的核心目标是控制资源的访问速率,以防止系统过载。在不同的业务场景下,限流的需求和实现方式也有所不同。例如,在Web服务中,限流可以用于控制用户请求的频率,以防止服务被滥用;在API网关中,限流可以用于保护后端服务不受过多请求的影响。限流算法的设计需要考虑到实际业务的特点,如请求的分布、峰值流量的大小等。

基于计数器的固定窗口限流

实现原理

固定窗口限流是一种最简单的限流方式。它将时间划分为固定大小的窗口,通常是秒或分钟级别的。在每个窗口内,统计请求的次数,并与预设的阈值进行比较。如果请求次数超过阈值,则拒绝后续请求,直到下一个窗口开始。这种限流方式简单直观,易于实现,但可能存在边界问题,即在窗口切换时请求量可能会突然增加。

适用场景

固定窗口限流适用于流量较为均匀的场景,适合简单的限流需求。例如,对于一个小型网站,如果其访问量相对稳定,没有明显的高峰和低谷,那么固定窗口限流可能是一个合适的选择。

实现步骤

  1. 使用Redis的INCR命令记录每个窗口的请求次数。
  2. 设置键的过期时间为窗口时长。
  3. 判断请求次数是否超过阈值。

代码实现

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;import java.util.concurrent.TimeUnit;@Service
public class FixedWindowRateLimiter {@Autowiredprivate StringRedisTemplate redisTemplate;public boolean isAllowed(String key, int limit, int windowSeconds) {String redisKey = "rate_limit:" + key;Long count = redisTemplate.opsForValue().increment(redisKey);if (count == 1) {redisTemplate.expire(redisKey, windowSeconds, TimeUnit.SECONDS);}return count <= limit;}
}

缺点

固定窗口限流的主要缺点是边界问题。在窗口切换时,请求量可能会突然增加,导致短时间内的流量突增。此外,如果请求分布不均匀,固定窗口限流可能无法有效控制流量。

基于滑动窗口的限流

实现原理

滑动窗口限流通过更精细地统计一定时间范围内的请求,避免了固定窗口限流的边界问题。它不是将时间划分为固定大小的窗口,而是维护一个滑动的时间窗口,通常使用有序集合(ZSET)来实现。在这个窗口内,记录每个请求的时间戳,并在每次请求时移除超出窗口的请求记录。这样,可以更准确地控制请求的速率,避免了固定窗口限流的边界问题。

适用场景

滑动窗口限流适用于对流量分布较为敏感的场景。例如,在一些高流量的Web服务中,请求可能在某些时间段内集中到达,而在其他时间段内相对较少。滑动窗口限流可以更准确地控制这些请求,避免因固定窗口限流导致的流量突增问题。

实现步骤

  1. 使用Redis的有序集合(ZSET)存储每次请求的时间戳。
  2. 每次请求时,移除集合中超出时间窗口的记录。
  3. 统计集合中剩余的记录数,判断是否超出阈值。

代码实现

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;import java.time.Instant;
import java.util.concurrent.TimeUnit;@Service
public class SlidingWindowRateLimiter {@Autowiredprivate StringRedisTemplate redisTemplate;public boolean isAllowed(String key, int limit, int windowSeconds) {String redisKey = "rate_limit:" + key;long currentTime = Instant.now().getEpochSecond();long windowStart = currentTime - windowSeconds;// 添加当前请求时间戳到ZSET中redisTemplate.opsForZSet().add(redisKey, String.valueOf(currentTime), currentTime);// 移除ZSET中超出时间窗口的请求redisTemplate.opsForZSet().removeRangeByScore(redisKey, 0, windowStart);// 获取当前窗口内的请求数量Long count = redisTemplate.opsForZSet().zCard(redisKey);return count != null && count <= limit;}
}

优点

滑动窗口限流较好地解决了固定窗口限流的边界问题,能够更准确地控制请求的速率。

缺点

滑动窗口限流的实现比固定窗口限流更复杂,对Redis的性能要求更高。特别是在高并发场景下,需要频繁地更新有序集合,可能会对Redis的性能产生影响。

基于令牌桶算法的限流

实现原理

令牌桶算法是最常用的限流算法之一。其核心思想是按照固定的速率向桶中放入令牌,用户每次请求需要获取一个令牌,如果桶为空,则拒绝请求。令牌桶算法可以控制请求的速率,并且支持突发流量。当桶中的令牌不足时,请求会被阻塞或拒绝,直到桶中有可用的令牌。

适用场景

令牌桶算法适用于对流量速率有严格控制的场景,例如API网关、秒杀系统等。这些场景下,需要精确控制请求的速率,并且能够处理突发的流量。

实现步骤

  1. 初始化令牌桶大小和放令牌的速率。
  2. 使用Redis的Lua脚本实现原子操作,保证线程安全。
  3. 判断是否有足够令牌满足请求。

Lua脚本实现

local key = KEYS[1]
local capacity = tonumber(ARGV[1])
local tokens = tonumber(ARGV[2])
local now = tonumber(ARGV[3])
local refillTime = tonumber(ARGV[4])local currentTokens = tonumber(redis.call("get", key)) or capacity
local lastRefillTime = tonumber(redis.call("get", key .. ":time")) or nowlocal timeElapsed = now - lastRefillTime
local refillTokens = math.floor(timeElapsed / refillTime)currentTokens = math.min(capacity, currentTokens + refillTokens)
if currentTokens > 0 thenredis.call("set", key, currentTokens - 1)redis.call("set", key .. ":time", now)return 1
elsereturn 0
end

Java实现

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;import java.util.Collections;@Service
public class TokenBucketRateLimiter {@Autowiredprivate StringRedisTemplate redisTemplate;private static final String LUA_SCRIPT = "-- Lua Script Here (见上文)";public boolean isAllowed(String key, int capacity, int tokens, int refillTime) {DefaultRedisScript<Long> script = new DefaultRedisScript<>(LUA_SCRIPT, Long.class);Long result = redisTemplate.execute(script,Collections.singletonList("rate_limit:" + key),String.valueOf(capacity),String.valueOf(tokens),String.valueOf(System.currentTimeMillis() / 1000),String.valueOf(refillTime) );return result != null && result == 1;
}

优点

令牌桶算法能够精确控制流量,并且支持突发流量处理。

缺点

令牌桶算法的实现较为复杂,需要Lua脚本支持。在高并发场景下,对Redis的性能要求较高。

总结

限流是确保分布式系统稳定性的关键技术之一。通过使用Redis,我们可以高效地实现限流功能。本文介绍了三种基于Redis的限流方法:固定窗口限流、滑动窗口限流和令牌桶算法限流,每种方法都有其适用场景和优缺点。固定窗口限流简单易实现,但可能存在边界问题;滑动窗口限流更精确,但实现复杂度和性能要求更高;令牌桶算法能够精确控制流量,支持突发流量,但实现最为复杂。选择合适的限流方案,可以有效地保护系统不受流量冲击,提高系统的可用性和用户体验。

在实际应用中,需要根据业务特点和流量分布选择合适的限流策略。例如,对于需要严格速率控制的API服务,令牌桶算法可能是更好的选择;而对于相对简单的应用,固定窗口限流可能已经足够。此外,限流策略的调整也是一个动态的过程,需要根据系统的运行情况和业务需求进行调整。

限流技术的发展也在不断进步,新的算法和工具不断涌现。例如,一些云服务提供商提供了基于机器学习的智能限流服务,可以根据实时流量动态调整限流策略。随着技术的发展,限流技术将更加智能化和精细化,为分布式系统的稳定性和性能提供更有力的保障。


http://www.ppmy.cn/server/152568.html

相关文章

每天40分玩转Django:实操博客应用

实操博客应用 一、内容概述 模块重要程度主要内容项目初始化⭐⭐⭐⭐创建项目和应用模型设计⭐⭐⭐⭐⭐文章、评论、用户模型视图实现⭐⭐⭐⭐⭐增删改查功能模板开发⭐⭐⭐⭐页面布局和样式用户认证⭐⭐⭐⭐⭐用户登录和权限 二、项目结构 blog_project/ ├── blog/ │ …

Linux入侵排查

1.查看有哪些用户登录了服务器 命令&#xff1a;who 如果存在可疑用户&#xff0c;使用kill pid杀死对应进程 2.查看是否新增可疑用户 命令&#xff1a;cat /etc/passwd 3.排查木马 命令&#xff1a;netstat -anp ps -x 查看是否有可疑外联ip或域名&#xff0c;然后根据…

新版国标GB28181设备端Android版EasyGBD支持国标GB28181-2022,支持语音对讲,支持位置上报,开源在Github

经过近3个月的迭代开发&#xff0c;新版本的国标GB28181设备端EasyGBD安卓Android版终于在昨天发布到Github了&#xff0c;最新的EasyGBD支持了国标GB28181-2022版&#xff0c;还支持了语音对讲、位置上报、本地录像等功能&#xff0c;比原有GB28181-2016版的EasyGBD更加高效、…

我的性能优化经验

专业方向&#xff1a;App cpu/memory/gpu/流畅度/响应时间的优化&#xff0c;Anr&#xff0c;Framework CarPowerManagementService模块的&#xff08;STR&#xff09;&#xff0c;从0~1完成性能监控体系搭建&#xff0c;完成3大版本迭代高质量性能交付 响应时间&#xff1a; …

【UE4】角色御剑飞行的蓝图实现

沉沉更鼓急&#xff0c;渐渐人声绝 吹灯窗更明&#xff0c;月照一天雪 UE4简单的实现御剑飞行的功能 契子✨ 所谓的御剑飞行的原理就跟 《御板》 飞行的原理差不多&#xff0c;不过是在人物脚上插把剑在飞行的时候显示出来罢了。简单来讲就是只要渲染做的足够牛&#xff0c;土鸡…

Webrtc音频模块(四) 音频采集

音频的采集还是封装在AudioDeviceWindowsCore中&#xff0c;相关的Core Audio API接口是下面几个&#xff1a; IAudioClient* _ptrClientIn IAudioCaptureClient* _ptrCaptureClient rtc::scoped_refptr<IMediaObject> _dmo rtc::scoped_refptr<IMediaBuffer> _me…

Java-32 深入浅出 Spring - IoC 基础 启动IoC 纯注解方式 SpringConfig web.xml

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 大数据篇正在更新&#xff01;https://blog.csdn.net/w776341482/category_12713819.html 目前已经更新到了&#xff1a; MyBatis&#xff…

【贪心算法】贪心算法六

贪心算法六 1.坏了的计算器2.合并区间3.无重叠区间4.用最少数量的箭引爆气球 点赞&#x1f44d;&#x1f44d;收藏&#x1f31f;&#x1f31f;关注&#x1f496;&#x1f496; 你的支持是对我最大的鼓励&#xff0c;我们一起努力吧!&#x1f603;&#x1f603; 1.坏了的计算器 …