.NET Redis限制接口请求频率 滑动窗口算法

在.NET中使用Redis来限制接口请求频率(每10秒只允许请求一次)

NuGet setup

StackExchange.Redis

实现速率限制逻辑

在控制器或服务层中,编写Redis速率限制计数器。
设置Redis键

为每个用户或每个IP地址设置一个唯一的键。这个键将用于存储最后一次请求的时间戳和/请求计数。
检查时间戳

当请求到达时,从Redis中获取该键的值(时间戳)。如果键不存在或时间戳超过10秒,则允许请求并更新键的值(设置为当前时间戳)。
处理超过速率的请求

如果时间戳在10秒内,则拒绝或限制该请求(返回限制状态码)。

csharp 复制代码
   private static readonly Lazy<ConnectionMultiplexer> LazyConnection = new Lazy<ConnectionMultiplexer>(() =>
   {
       // 配置Redis连接字符串 "localhost,abortConnect=false"  
       return ConnectionMultiplexer.Connect("localhost:6379");
   });

   private static ConnectionMultiplexer Connection => LazyConnection.Value;

   private static IDatabase Db => Connection.GetDatabase();

   public async Task<ActionResult> MyAction()
   {

       IPAddress clientIpAddress = HttpContext.Connection.RemoteIpAddress;
       string ipAddress = clientIpAddress.ToString();
       string redisKey = $"rate-limit:{ipAddress}"; // 构建Redis键名  
  
       // 获取当前时间戳(可以是Unix时间戳或任何你选择的格式)  
       long currentTimestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds();

       // 尝试从Redis获取时间戳  
       var redisValue = await Db.StringGetAsync(redisKey);
       long lastTimestamp = redisValue.HasValue ? (long)redisValue : 0;

       // 检查是否超过10秒  
       if (currentTimestamp - lastTimestamp >= 10)
       {
           // 如果超过10秒,则允许请求并更新Redis键  
           await Db.StringSetAsync(redisKey, currentTimestamp, TimeSpan.FromSeconds(10)); // 设置键的过期时间为10秒  
                                                                                        
           return Content("Request allowed.");
       }
       else
       {
           // 如果未超过10秒,则拒绝请求  
           HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.TooManyRequests)
           {
               ReasonPhrase = "Too Many Requests",
               Content = new StringContent("Rate limit exceeded. Please try again later.")
           };
           // 处理请求
           return Content("Please try again later. ");// throw new HttpResponseException(response); // 或者返回自定义的ActionResult  
       }
   }

扩展为参数

csharp 复制代码
MyAction(string p)
//...
string redisKey = $"rate-limit:{p}";

请求

/MyAction?p=2

/MyAction?p=3

滑动窗口算法

滑动窗口算法(Sliding Window Algorithm)是一种用于解决字符串/数组 问题的算法,它通过维护一个窗口(即一个连续的子串或子数组),并在字符串或数组上滑动这个窗口来寻找满足特定条件的子串或子数组。以下是滑动窗口算法的主要内容和特点:

维护窗口 :通过两个指针(左指针和右指针)来定义窗口的边界。
移动窗口 :通过移动右指针来扩展窗口,同时根据问题的要求调整左指针来缩小窗口。
更新信息:在窗口滑动的过程中,根据需要更新一些数据结构(如哈希表)来保存所需的信息。

实现方法
步骤1.初始化 :定义左指针和右指针,并初始化它们的位置。
步骤2.扩展窗口 :向右移动右指针,扩展窗口,同时更新所需的信息(如字符频率的哈希表)。
步骤3.检查条件 :当窗口满足特定条件时,开始收缩窗口。
步骤4.收缩窗口 :向右移动左指针,缩小窗口,同时更新所需的信息。
步骤5.更新最优解 :在收缩窗口的过程中,不断更新最优解(如最长子串、最短子串等)。
重复步骤:重复步骤2到步骤5,直到右指针到达字符串或数组的末尾。

在Redis中维护一个窗口内的请求时间戳列表,而不是仅仅存储最后一次请求的时间戳。移除超过窗口大小的时间戳。检查剩余的时间戳数是否超过了最大请求数 MaxRequests。如果超过,则返回超过的响应;否则,记录当前时间戳并允许请求。

csharp 复制代码
			private const int MaxRequests = 5; // 最大请求数
			private const int WindowSizeInSeconds = 10; // 窗口大小(秒)
			//...

            // 获取Redis中存储的时间戳列表
            var redisValue = await Db.ListRangeAsync(redisKey);
            var timestamps = redisValue.Select(value => (long)value).ToList();

            // 移除窗口之外的时间戳
            timestamps = timestamps.Where(timestamp => currentTimestamp - timestamp <= WindowSizeInSeconds).ToList();

            if (timestamps.Count >= MaxRequests)
            {
                // 如果请求数超过限制,则拒绝请求
                HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.TooManyRequests)
                {
                    ReasonPhrase = "Too Many Requests",
                    Content = new StringContent("Rate limit exceeded. Please try again later.")
                };
                return Content("Please try again later.");
            }
            else
            {
                // 如果请求数未超过限制,则允许请求并记录当前时间戳
                timestamps.Add(currentTimestamp);
                await Db.ListRightPushAsync(redisKey, timestamps.Select(timestamp => (RedisValue)timestamp).ToArray());
                await Db.KeyExpireAsync(redisKey, TimeSpan.FromSeconds(WindowSizeInSeconds)); // 设置键的过期时间为窗口大小

                return Content("Request allowed.");
            }

End

相关推荐
tiancao2221 小时前
安装3DS MAX 2026后,无法运行,提示缺少.net core的解决方案
.net·.netcore·3dsmax
FL162386312911 小时前
Windows Server2012 R2 安装.NET Framework 3.5
.net
CodeCraft Studio15 小时前
Aspose.Words for .NET 25.7:支持自建大语言模型(LLM),实现更安全灵活的AI文档处理功能
人工智能·ai·语言模型·llm·.net·智能文档处理·aspose.word
要记得喝水15 小时前
C#某公司面试题(含题目和解析)--1
开发语言·windows·面试·c#·.net
SEO-狼术16 小时前
Aqua Data Studio 25.5
.net
MasterNeverDown1 天前
.net 微服务jeager链路跟踪
微服务·架构·.net
喵叔哟2 天前
51.【.NET8 实战--孢子记账--从单体到微服务--转向微服务】--新增功能--登录注册扩展
数据库·微服务·.net
时光追逐者2 天前
.NET 使用 CsvHelper 快速读取和写入 CSV 文件
c#·.net·.net core·csv
界面开发小八哥2 天前
文档控件DevExpress Office File API v25.1新本亮点:重磅升级各类API
c#·.net·界面控件·devexpress·ui开发
追逐时光者3 天前
.NET 使用 CsvHelper 快速读取和写入 CSV 文件
后端·.net