.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

相关推荐
唐青枫1 小时前
C#.NET NCrontab 深入解析:轻量级 Cron 表达式解析器
c#·.net
追逐时光者13 小时前
一个基于 .NET 8 + Vue3 实现的极简 RABC 权限管理系统
后端·.net
.NET修仙日记1 天前
C#/.NET 微服务架构:从入门到精通的完整学习路线
微服务·c#·.net·.net core·分布式架构·技术进阶
追逐时光者2 天前
全面的 C#/.NET 图表构建解决方案,助力快速实现图表开发需求!
后端·.net
唐青枫2 天前
C#.NET ArrayPool 深入解析:高性能内存池的实现与应用
c#·.net
一个专注写bug的小白猿3 天前
.net实现ftp传输文件保姆教程
后端·c#·.net
初级代码游戏3 天前
winform迁移:从.net framework 到 .net9
.net·迁移·.net framework
TeamDev3 天前
使用 Shadcn UI 构建 C# 桌面应用
前端·后端·.net
CodeCraft Studio3 天前
CADSoftTools发布两款重要更新:CAD VCL Multiplatform 16.2 与 CAD .NET 16全新发布
.net·cad vcl·cad .net·cad文件格式解析·cad文件编辑·cad文件查看器
唐青枫3 天前
C#.NET FluentSqlKata 全面解析:基于链式语法的动态 SQL 构建
c#·.net