Azure Web App(ASP.NET Web API)如何限制请求数并避免端点重复触发
为Azure Web App(ASP.NET Web API)设置请求数量限制的方案
针对你提到的耗时端点防重复触发需求,可通过应用内限流、Azure平台限流、分布式锁防护三种维度实现,以下是具体实现方式:
一、ASP.NET Web API 应用内限流
1. .NET 7+ 内置速率限制组件
.NET 7及以上版本自带速率限制中间件,可精准控制特定端点的请求频率:
// Program.cs 中配置限流规则 builder.Services.AddRateLimiter(options => { // 为耗时端点定义固定窗口限流规则 options.AddFixedWindowLimiter("LongRunningEndpoint", opt => { opt.Window = TimeSpan.FromMinutes(5); // 统计窗口时长 opt.PermitLimit = 1; // 窗口内允许的最大请求数 opt.QueueLimit = 0; // 禁止排队,超出直接拒绝 opt.QueueProcessingOrder = QueueProcessingOrder.OldestFirst; }); }); // 全局应用限流(或针对单个端点单独配置) app.MapControllers().RequireRateLimiting("LongRunningEndpoint"); // 单个端点示例 app.MapGet("/api/heavy-task", async () => { // 模拟耗时逻辑 await Task.Delay(TimeSpan.FromMinutes(2)); return Results.Ok("Task finished"); }).RequireRateLimiting("LongRunningEndpoint");
2. 第三方限流库 AspNetCoreRateLimit
适合更复杂的限流场景(如IP维度、自定义规则):
// Program.cs 配置 builder.Services.AddMemoryCache(); builder.Services.AddInMemoryRateLimiting(); builder.Services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>(); builder.Services.Configure<IpRateLimitOptions>(options => { options.GeneralRules = new List<RateLimitRule> { new RateLimitRule { Endpoint = "GET:/api/heavy-task", Period = "5m", Limit = 1 } }; }); app.UseIpRateLimiting();
二、Azure App Service 平台层面限流
通过Azure门户配置全局或路径级别的限流(仅Basic及以上App Service Plan支持):
- 进入目标App Service的网络 > 配置访问限制
- 添加规则时,选择速率限制选项,设置单位时间内的请求上限,可指定目标路径(如
/api/heavy-task)
三、耗时端点重复触发防护(分布式锁)
如果需要确保同一任务在执行期间不会被重复触发,可结合Azure Redis Cache实现分布式锁:
private readonly IDistributedCache _redisCache; public HeavyTaskController(IDistributedCache redisCache) { _redisCache = redisCache; } [HttpGet("heavy-task")] public async Task<IActionResult> ExecuteHeavyTask(string taskUniqueId) { var lockKey = $"HeavyTaskLock:{taskUniqueId}"; var lockValue = Guid.NewGuid().ToString(); var lockExpiry = new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10) // 锁超时时间需长于任务耗时 }; // 尝试获取锁,仅当锁不存在时才执行 var lockAcquired = await _redisCache.StringSetAsync(lockKey, lockValue, lockExpiry, When.NotExists); if (!lockAcquired) { return Conflict("Task is already in progress"); } try { // 执行耗时业务逻辑 await Task.Delay(TimeSpan.FromMinutes(2)); return Ok("Task completed successfully"); } finally { // 验证锁归属后释放,避免误删其他请求的锁 var currentLockValue = await _redisCache.GetStringAsync(lockKey); if (currentLockValue == lockValue) { await _redisCache.RemoveAsync(lockKey); } } }
内容的提问来源于stack exchange,提问作者Vish
相关产品推荐
相关产品推荐

