ASP.NET Core Web API 接口限流

  • ASP.NET Core Web API 接口限流已关闭评论
  • 170 次浏览
  • A+
所属分类:.NET技术
摘要

ASP.NET Core Web API 接口限流、限制接口并发数量,我也不知道自己写的有没有问题,抛砖引玉、欢迎来喷!


前言

ASP.NET Core Web API 接口限流、限制接口并发数量,我也不知道自己写的有没有问题,抛砖引玉、欢迎来喷!

需求

  1. 写了一个接口,参数可以传多个人员,也可以传单个人员,时间范围限制最长一个月。简单来说,当传单个人员时,接口耗时很短,当传多个人员时,一般人员会较多,接口耗时较长,一般耗时几秒。
  2. 当传多个人员时,并发量高时,接口的耗时就很长了,比如100个用户并发请求,耗时可长达几十秒,甚至1分钟。
  3. 所以需求是,当传单个人员时,不限制。当传多个人员时,限制并发数量。如果并发用户数少于限制数,那么所有用户都能成功。如果并发用户数,超出限制数,那么超出的用户请求失败,并提示"当前进行XXX查询的用户太多,请稍后再试"。
  4. 这样也可以减轻被请求的ES集群的压力。

说明

  1. 使用的是.NET6
  2. 我知道有人写好了RateLimit中间件,但我暂时还没有学会怎么使用,能否满足我的需求,所以先自己实现一下。

效果截图

下面是使用jMeter并发测试时,打的接口日志:
ASP.NET Core Web API 接口限流

代码

RateLimitInterface

接口参数的实体类要继承该接口

using JsonA = Newtonsoft.Json; using JsonB = System.Text.Json.Serialization;  namespace Utils {     /// <summary>     /// 限速接口     /// </summary>     public interface RateLimitInterface     {         /// <summary>         /// 是否限速         /// </summary>         [JsonA.JsonIgnore]         [JsonB.JsonIgnore]         bool IsLimit { get; }     } } 

接口参数实体类

继承RateLimitInterface接口,并实现IsLimit属性

public class XxxPostData : RateLimitInterface {     ...省略      /// <summary>     /// 是否限速     /// </summary>     [JsonA.JsonIgnore]     [JsonB.JsonIgnore]     public bool IsLimit     {         get         {             if (peoples.Count > 2) //限速条件,自己定义             {                 return true;             }             return false;         }     } } 

RateLimitAttribute

作用:标签打在接口方法上,并设置并发数量

namespace Utils {     /// <summary>     /// 接口限速     /// </summary>     public class RateLimitAttribute : Attribute     {         private Semaphore _sem;          public Semaphore Sem         {             get             {                 return _sem;             }         }          public RateLimitAttribute(int limitCount = 1)         {             _sem = new Semaphore(limitCount, limitCount);         }     } } 

使用RateLimitAttribute

标签打在接口方法上,并设置并发数量。
服务器好像是24核的,并发限制为8应该没问题。

[HttpPost] [Route("[action]")] [RateLimit(8)] public async Task<List<XxxInfo>> Query([FromBody] XxxPostData data) {     ...省略 } 

限制接口并发量的拦截器RateLimitFilter

/// <summary> /// 接口限速 /// </summary> public class RateLimitFilter : ActionFilterAttribute {     public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)     {         Type controllerType = context.Controller.GetType();         object arg = context.ActionArguments.Values.ToList()[0];         var rateLimit = context.ActionDescriptor.EndpointMetadata.OfType<RateLimitAttribute>().FirstOrDefault();          bool isLimit = false; //是否限速         if (rateLimit != null && arg is RateLimitInterface) //接口方法打了RateLimitAttribute标签并且参数实体类实现了RateLimitInterface接口时才限速,否则不限速         {             RateLimitInterface model = arg as RateLimitInterface;             if (model.IsLimit) //满足限速条件             {                 isLimit = true;                 Semaphore sem = rateLimit.Sem;                  if (sem.WaitOne(0))                 {                     try                     {                         await next.Invoke();                     }                     catch                     {                         throw;                     }                     finally                     {                         sem.Release();                     }                 }                 else                 {                     var routeList = context.RouteData.Values.Values.ToList();                     routeList.Reverse();                     var route = string.Join('/', routeList.ConvertAll(a => a.ToString()));                     var msg = $"当前访问{route}接口的用户数太多,请稍后再试";                     LogUtil.Info(msg);                     context.Result = new ObjectResult(new ApiResult                     {                         code = (int)HttpStatusCode.BadRequest,                         message = msg                     });                 }             }         }          if (!isLimit)         {             await next.Invoke();         }     } } 

注册拦截器

//拦截器 builder.Services.AddMvc(options => {     ...省略      options.Filters.Add<RateLimitFilter>(); }); 

使用jMeter进行压力测试

测试结果:

  1. 被限速的接口,满足限速条件的调用并发量大时,部分用户成功,部分用户提示当前查询的人多请稍后再试。但不影响未满足限速条件的传参调用,也不影响其它未限速接口的调用。
  2. 测试的所有接口、所有查询参数条件的调用,耗时稳定,大量并发时,不会出现接口耗时几十秒甚至1分钟的情况。