翼度科技»论坛 编程开发 .net 查看内容

ASP.NET Core Web API 接口限流

9

主题

9

帖子

27

积分

新手上路

Rank: 1

积分
27
前言

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


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


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

下面是使用jMeter并发测试时,打的接口日志:

代码

RateLimitInterface

接口参数的实体类要继承该接口
  1. using JsonA = Newtonsoft.Json;
  2. using JsonB = System.Text.Json.Serialization;
  3. namespace Utils
  4. {
  5.     /// <summary>
  6.     /// 限速接口
  7.     /// </summary>
  8.     public interface RateLimitInterface
  9.     {
  10.         /// <summary>
  11.         /// 是否限速
  12.         /// </summary>
  13.         [JsonA.JsonIgnore]
  14.         [JsonB.JsonIgnore]
  15.         bool IsLimit { get; }
  16.     }
  17. }
复制代码
接口参数实体类

继承RateLimitInterface接口,并实现IsLimit属性
  1. public class XxxPostData : RateLimitInterface
  2. {
  3.     ...省略
  4.     /// <summary>
  5.     /// 是否限速
  6.     /// </summary>
  7.     [JsonA.JsonIgnore]
  8.     [JsonB.JsonIgnore]
  9.     public bool IsLimit
  10.     {
  11.         get
  12.         {
  13.             if (peoples.Count > 2) //限速条件,自己定义
  14.             {
  15.                 return true;
  16.             }
  17.             return false;
  18.         }
  19.     }
  20. }
复制代码
RateLimitAttribute

作用:标签打在接口方法上,并设置并发数量
  1. namespace Utils
  2. {
  3.     /// <summary>
  4.     /// 接口限速
  5.     /// </summary>
  6.     public class RateLimitAttribute : Attribute
  7.     {
  8.         private Semaphore _sem;
  9.         public Semaphore Sem
  10.         {
  11.             get
  12.             {
  13.                 return _sem;
  14.             }
  15.         }
  16.         public RateLimitAttribute(int limitCount = 1)
  17.         {
  18.             _sem = new Semaphore(limitCount, limitCount);
  19.         }
  20.     }
  21. }
复制代码
使用RateLimitAttribute

标签打在接口方法上,并设置并发数量。
服务器好像是24核的,并发限制为8应该没问题。
  1. [HttpPost]
  2. [Route("[action]")]
  3. [RateLimit(8)]
  4. public async Task<List<XxxInfo>> Query([FromBody] XxxPostData data)
  5. {
  6.     ...省略
  7. }
复制代码
限制接口并发量的拦截器RateLimitFilter
  1. /// <summary>
  2. /// 接口限速
  3. /// </summary>
  4. public class RateLimitFilter : ActionFilterAttribute
  5. {
  6.     public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
  7.     {
  8.         Type controllerType = context.Controller.GetType();
  9.         object arg = context.ActionArguments.Values.ToList()[0];
  10.         var rateLimit = context.ActionDescriptor.EndpointMetadata.OfType<RateLimitAttribute>().FirstOrDefault();
  11.         bool isLimit = false; //是否限速
  12.         if (rateLimit != null && arg is RateLimitInterface) //接口方法打了RateLimitAttribute标签并且参数实体类实现了RateLimitInterface接口时才限速,否则不限速
  13.         {
  14.             RateLimitInterface model = arg as RateLimitInterface;
  15.             if (model.IsLimit) //满足限速条件
  16.             {
  17.                 isLimit = true;
  18.                 Semaphore sem = rateLimit.Sem;
  19.                 if (sem.WaitOne(0))
  20.                 {
  21.                     try
  22.                     {
  23.                         await next.Invoke();
  24.                     }
  25.                     catch
  26.                     {
  27.                         throw;
  28.                     }
  29.                     finally
  30.                     {
  31.                         sem.Release();
  32.                     }
  33.                 }
  34.                 else
  35.                 {
  36.                     var routeList = context.RouteData.Values.Values.ToList();
  37.                     routeList.Reverse();
  38.                     var route = string.Join('/', routeList.ConvertAll(a => a.ToString()));
  39.                     var msg = $"当前访问{route}接口的用户数太多,请稍后再试";
  40.                     LogUtil.Info(msg);
  41.                     context.Result = new ObjectResult(new ApiResult
  42.                     {
  43.                         code = (int)HttpStatusCode.BadRequest,
  44.                         message = msg
  45.                     });
  46.                 }
  47.             }
  48.         }
  49.         if (!isLimit)
  50.         {
  51.             await next.Invoke();
  52.         }
  53.     }
  54. }
复制代码
注册拦截器
  1. //拦截器
  2. builder.Services.AddMvc(options =>
  3. {
  4.     ...省略
  5.     options.Filters.Add<RateLimitFilter>();
  6. });
复制代码
使用jMeter进行压力测试

测试结果:

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

来源:https://www.cnblogs.com/s0611163/archive/2023/03/09/17199379.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具