.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

相关推荐
九鼎科技-Leo9 小时前
了解 .NET 运行时与 .NET 框架:基础概念与相互关系
windows·c#·.net
九鼎科技-Leo11 小时前
什么是 ASP.NET Core?与 ASP.NET MVC 有什么区别?
windows·后端·c#·asp.net·mvc·.net
.net开发11 小时前
WPF怎么通过RestSharp向后端发请求
前端·c#·.net·wpf
九鼎科技-Leo15 小时前
在 C# 中,ICollection 和 IList 接口有什么区别?
windows·c#·.net
时光追逐者16 小时前
C#/.NET/.NET Core学习路线集合,学习不迷路!
开发语言·学习·c#·asp.net·.net·.netcore·微软技术
Crazy Struggle19 小时前
.NET 全功能流媒体管理控制接口平台
.net·开源项目·流媒体
.net开发19 小时前
WPF使用prism框架发布订阅实现消息提示
c#·.net·wpf
慧都小妮子1 天前
FastReport将停止 .NET Framework 上的 WebReport 更新
.net·报表控件·fastreport
九鼎科技-Leo1 天前
在 C# 中,如何实现观察者模式?
观察者模式·c#·.net