并发请求太多,服务器崩溃了?试试使用 ASP.NET Core Web API 操作筛选器对请求进行限流
前言
请求限流(Rate Limiting)主要是一种用于控制客户端对服务器的请求频率的机制。
其目的是限制客户端在一定时间内可以发送的请求数量,保护服务器免受过多请求的影响,确保系统的稳定性和可靠性。
请求限流通常会基于以下几个因素来进行限制:
- 时间窗口:规定了在多长时间内允许的请求次数
- 请求配额:在时间窗口内允许的最大请求数量
- 客户端标识:根据客户端的 IP 地址、用户标识或其他标识符来进行限流
请求限流技术可以应用在很多场景,本文主要讲述 ASP.NET Core Web API 如何使用操作筛选器对请求进行限流。
Step By Step 步骤
-
创建一个ASP.NET Core Web API 项目
-
编写自定义的操作筛选器 RateLimitFilter,实现 "1s内只允许最多有一个来自同一个IP地址的请求"(留意注释)
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.Extensions.Caching.Memory; public class RateLimitFilter : IAsyncActionFilter { private readonly IMemoryCache memCache; // 注入的IMemoryCache public RateLimitFilter(IMemoryCache memCache) { this.memCache = memCache; } public Task OnActionExecutionAsync( ActionExecutingContext context, ActionExecutionDelegate next) { // 通过注入的 IMemoryCache 来记录用户上一次访问的时间戳 // 在分布式系统下可以改用分布式缓存来代替内存缓存 string removeIP = context.HttpContext.Connection.RemoteIpAddress!.ToString(); string cacheKey = $"LastVisitTick_{removeIP}"; // 从缓存中获取这个客户端IP地址上一次访问服务器的时间 long? lastTick = memCache.Get<long?>(cacheKey); if (lastTick == null || Environment.TickCount64 - lastTick > 1000) { // 如果缓存中不存在上一次访问时间或者上一次访问时间距离现在已经超过 1s,则通过 next 来执行后面的筛选器 memCache.Set(cacheKey, Environment.TickCount64, TimeSpan.FromSeconds(10)); return next(); } else { // 否则说明 IP 频繁访问,不执行 next,相当于终止操作方法的执行 context.Result = new ContentResult { StatusCode = 429 }; return Task.CompletedTask; } } }
代码中的内存缓存和分布式缓存可以参考本人之前文章《看看 Asp.net core Webapi 项目如何优雅地使用内存缓存》和《看看 Asp.net core Webapi 项目如何优雅地使用分布式缓存》
-
打开 Program.cs,注册这个操作筛选器
using Microsoft.AspNetCore.Mvc; var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllers(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); // 注册内存缓存服务 builder.Services.AddMemoryCache(); // 注册请求限流过滤器 builder.Services.Configure<MvcOptions>(options => { options.Filters.Add<RateLimitFilter>(); }); var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.Run();
测试
启动项目,并且访问接口,如果访问频率不高的话,接口能够正常工作。
如果访问频率很高的话,服务器就会提示 "Only once per second!"
总结
在操作筛选器中,通过 await next()
来执行下一个筛选器,
如果没有下一个筛选器,程序就会执行目标操作方法。
如果不调用 await next()
,就可以终止操作方法的执行了
我是老杨,一个执着于编程乐趣、至今奋斗在一线的 10年+ 资深研发老鸟,是软件项目管理师,也是快乐的程序猿,持续免费分享全栈实用编程技巧、项目管理经验和职场成长心得!欢迎关注老杨的公众号(名称:代码掌控者),和你共同探索代码世界的奥秘!