什么是API限流:

API 限流是限制用户在一定时间内 API 请求数量的过程。应用程序编程接口 (API) 充当用户和软件应用程序之间的网关。例如,当用户单击社交媒体上的发布按钮时,点击该按钮会触发 API 调用。此 API 与社交媒体应用程序的网络服务器进行交互,并执行发布操作。此用户可以是人,也可以是其他软件应用程序。

为什么要限流:

API 是组织最大的资产之一。API 可帮助网站或移动应用程序的用户完成任务。随着用户数量的增加,网站或移动应用程序开始出现性能下降的迹象。因此,拥有更好连接或更快界面的用户可能会获得比其他用户更好的体验。API 限流是一种巧妙的解决方案,可帮助组织确保其 API 的合理使用。

API 限流还有助于抵御拒绝服务 (DoS) 攻击,在 DoS 攻击中,恶意用户发送大量请求以使网站或移动应用程序崩溃。随着在线用户数量的增加,企业需要实施 API 限流机制,以确保公平使用、数据安全并防止恶意攻击。

API限流的原理:

虽然 API 限流有多种算法,但以下是所有 API 限流算法的基本步骤:

1.客户端/用户调用与网络服务或应用程序交互的 API。

2.API 限流逻辑会检查当前请求是否超过允许的 API 调用次数。

3.如果请求在限制范围内,API 将照常执行并完成用户的任务。

4.如果请求超出限制,API 会向用户返回错误响应。

5.用户必须等待预先约定的时间段,或者付费才能进行更多的 API 调用。

这里有篇文章介绍很全面,可以看一看《API 限流技术探索与实践》

这个限流方案也是在百度收集整理而来,我这里采取的是滑动算法:

我们需要准备几个类:

1.ApiAuthorize类

ApiAuthorize继承于IAuthorizationFilter(授权过滤器),和IAuthorizationFilter相同的还有其他三种过滤器,合起来称为四大过滤器,

另外三个分别是IResourceFilter资源过滤器(缓存接口的数据),IActionFilter动作过滤器(记录操作日志),IExceptionFilter(错误过滤器)

IAuthorizationFilter

public class CtmAuthorizationFilterAttribute : Attribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
// context.HttpContext.User.Claims
context.HttpContext.Items["User"] = "HuangMing";
System.Console.WriteLine("OnAuthorization");
}
}

IResourceFilter

//Program.cs中注册缓存:
builder.Services.AddSingleton<IMemoryCache,MemoryCache>();
builder.Services.AddSingleton<IDistributedCache, MemoryDistributedCache>();
var app = builder.Build(); public class CtmResourceFilterAttribute : Attribute, IResourceFilter
{
private readonly IMemoryCache _cache; public CtmResourceFilterAttribute(IMemoryCache cache)
{
this._cache = cache;
} public void OnResourceExecuted(ResourceExecutedContext context)
{
var path = context.HttpContext.Request.Path.ToString();
if (context.Result != null)
{
var value = (context.Result as ObjectResult).Value.ToString();
_cache.Set(path, value,TimeSpan.FromHours(1));
}
} public void OnResourceExecuting(ResourceExecutingContext context)
{
var path = context.HttpContext.Request.Path.ToString();
var hasValue = _cache.TryGetValue(path, out object value);
if (hasValue)
{
context.Result = new ContentResult
{
Content = value.ToString()
};
}
}
}

IActionFilter

public class CtmActionFilterAttribute : Attribute, IActionFilter
{
public void OnActionExecuted(ActionExecutedContext context)
{
} public void OnActionExecuting(ActionExecutingContext context)
{
//从serviceProvider中获取Logger服务
var logger = context.HttpContext.RequestServices.GetService<ILogger<CtmActionFilterAttribute>>();
//获取路由地址
var path = context.HttpContext.Request.Path;
//从RouteData字典中获取控制器名称
var controller = context.RouteData.Values["controller"];
//从RouteData字典中获取动作名称
var action = context.RouteData.Values["action"];
//从ActionArguments中获取接口参数
var arguments = string.Join(",", context.ActionArguments);
logger.LogInformation($"访问的路由:{path},控制器是{controller},行为是{action},参数是{arguments}");
}
} //当过滤器中需要使用依赖注入时,在使用属性标注时,需要使用如下方式:
1.属性标注
[TypeFilter(typeof(CtmActionFilterAttribute))] 2.从容器中获取服务
var logger = context.HttpContext.RequestServices.GetService<ILogger<CtmActionFilterAttribute>>();

IActionFilter

public class CtmExceptionFilterAttribute : Attribute, IExceptionFilter
{
public void OnException(ExceptionContext context)
{
context.Result = new ContentResult{
Content =context.Exception.Message
};
}
}

现在编写自己的项目代码

ApiAuthorize

public class ApiAuthorize : IAuthorizationFilter
{
public async void OnAuthorization(AuthorizationFilterContext context)
{
if (context.Filters.Contains(new MyNoAuthentication()))
{
return;
} #region 用户请求限流
{
string ip = context.HttpContext.Connection.RemoteIpAddress.ToString();
var cotrollaction = context.ActionDescriptor;
string action = cotrollaction.RouteValues["action"].ToString();
string controller = cotrollaction.RouteValues["controller"].ToString();
if (string.IsNullOrWhiteSpace(ip) || string.IsNullOrWhiteSpace(controller) || string.IsNullOrWhiteSpace(action))
{
context.Result = new JsonResult("系统正忙,请稍微再试!");
return;
}
ip = ip + ":" + controller + ":" + action;
IPCacheInfoModel ipModel = IPCacheHelper.GetIPLimitInfo(ip);
if (!ipModel.IsVisit)
{
context.Result = new JsonResult("系统正忙,请稍微再试!");
return;
}
string ACting = controller + ":" + action;
IPCacheInfoModel ipModel2 = IPCacheHelper.GetIPLimitInfo(ACting); }
#endregion #endregion
} }

然后编写 MyAuthentication类

MyAuthentication

/// <summary>
/// 构造引用
/// </summary>
public class MyAuthentication : Attribute, IFilterMetadata
{
}
public class MyNoAuthentication : Attribute, IFilterMetadata
{
}

以上两个可以做限流也能做鉴权,数据签名认证等

如果需要限流,我们还需要三个类:

IPActionFilterAttribute 信息返回类

using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http.Controllers;
using System.Web.Http.Filters; namespace EvaluationSystem.XLAction
{
/// <summary>
/// 限制单个IP短时间内访问次数
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public class IPActionFilterAttribute : ActionFilterAttribute
{
/// <summary>
/// 限制单个IP短时间内访问次数
/// </summary>
/// <param name="actionContext"></param>
public override void OnActionExecuting(HttpActionContext actionContext)
{
string ip = actionContext.Request.ToString();
IPCacheInfoModel ipModel = IPCacheHelper.GetIPLimitInfo(ip);
if (!ipModel.IsVisit)
{
// Logger.Warn(string.Format("IP【{0}】被限制了【{1}】次数", ipModel.IP, ipModel.Limit));
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.BadRequest, "系统正忙,请稍微再试。");
return;
}
base.OnActionExecuting(actionContext);
} }
}

IPCacheHelper 请求记录类

using EvaluationSystem.HelpTool;
using EvaluationSystem.HelpTool.GetSYSValue;
using System;
using System.Collections.Generic; namespace EvaluationSystem.XLAction
{
/// <summary>
/// 限制单个IP访问次数
/// </summary>
public class IPCacheHelper
{
/// <summary>
/// IP缓存集合
/// </summary>
private static List<IPCacheInfoModel> dataList = new List<IPCacheInfoModel>(); private static object lockObj = new object();
//SQLHelp ht = new SQLHelp();
public static string maxTimes1 = GetConfig.GetConfiguration("XLAction:maxTimes");
public static string partSecond1 = GetConfig.GetConfiguration("XLAction:partSecond"); /// <summary>
/// 一段时间内,最大请求次数,必须大于等于1
///</summary>
private static int maxTimes = Convert.ToInt32(string.IsNullOrWhiteSpace(maxTimes1)? "0":maxTimes1); /// <summary>
/// 一段时间长度(单位秒),必须大于等于1
/// </summary>
private static int partSecond = Convert.ToInt32(string.IsNullOrWhiteSpace(partSecond1) ? "0" : partSecond1); /// <summary>
/// 请求被拒绝是否加入请求次数
/// </summary>
private static bool isFailAddIn = false; static IPCacheHelper()
{ }
/// <summary>
/// 设置时间,默认maxTimes=3, partSecond=30
/// </summary>
/// <param name="_maxTimes">最大请求次数</param>
/// <param name="_partSecond">请求单位时间</param>
public static void SetTime(int _maxTimes, int _partSecond)
{
maxTimes = _maxTimes;
partSecond = _partSecond;
} /// <summary>
/// 检测一段时间内,IP的请求次数是否可以继续请求和使用
/// </summary>
/// <param name="ip">ip</param>
/// <returns></returns>
public static bool CheckIsAble(string ip)
{
lock (lockObj)
{
var item = dataList.Find(p => p.IP == ip);
if (item == null)
{
item = new IPCacheInfoModel();
item.IP = ip;
item.ReqTime.Add(DateTime.Now);
dataList.Add(item);
return true;
}
else
{
if (item.ReqTime.Count > maxTimes)
{
item.ReqTime.RemoveAt(0);
}
var nowTime = DateTime.Now;
if (isFailAddIn)
{
#region 请求被拒绝也需要加入当次请求
item.ReqTime.Add(nowTime);
if (item.ReqTime.Count >= maxTimes)
{
if (item.ReqTime[0].AddSeconds(partSecond) > nowTime)
{
return false;
}
else
{
return true;
}
}
else
{
return true;
}
#endregion
}
else
{
#region 请求被拒绝就不需要加入当次请求了
if (item.ReqTime.Count >= maxTimes)
{
if (item.ReqTime[0].AddSeconds(partSecond) > nowTime)
{
return false;
}
else
{
item.ReqTime.Add(nowTime);
return true;
}
}
else
{
item.ReqTime.Add(nowTime);
return true;
}
#endregion
}
}
}
} /// <summary>
/// 检测一段时间内,IP的请求次数是否可以继续请求和使用
/// </summary>
/// <param name="ip">ip</param>
/// <returns></returns>
public static IPCacheInfoModel GetIPLimitInfo(string ip)
{
lock (lockObj)
{
var item = dataList.Find(p => p.IP == ip);
if (item == null) //IP开始访问
{
item = new IPCacheInfoModel();
item.IP = ip;
item.ReqTime.Add(DateTime.Now);
dataList.Add(item);
item.IsVisit = true; //可以继续访问 return item;
}
else
{
if (item.ReqTime.Count > maxTimes)
{
item.ReqTime.RemoveAt(0);
}
var nowTime = DateTime.Now;
if (isFailAddIn)
{
#region 请求被拒绝也需要加入当次请求
item.ReqTime.Add(nowTime); if (item.ReqTime.Count >= maxTimes)
{
if (item.ReqTime[0].AddSeconds(partSecond) > nowTime)
{
item.Limit++; //限制次数+1
item.IsVisit = false;//不能继续访问
return item;
}
else
{
item.IsVisit = true; //可以继续访问
return item; //单个IP30秒内 没有多次访问
}
}
else
{
item.IsVisit = true; //可以继续访问
return item; //单个IP访问次数没有达到max次数
}
#endregion
}
else
{
#region 请求被拒绝就不需要加入当次请求了
if (item.ReqTime.Count >= maxTimes)
{
if (item.ReqTime[0].AddSeconds(partSecond) > nowTime)
{
item.Limit++; //限制次数+1
item.IsVisit = false;//不能继续访问 return item;
}
else
{
item.ReqTime.Add(nowTime); item.IsVisit = true; //可以继续访问
return item;
}
}
else
{
item.ReqTime.Add(nowTime);
item.IsVisit = true; //可以继续访问 return item;
}
#endregion
}
}
}
}
}
}

IPCacheInfoModel 实体类

using System;
using System.Collections.Generic; namespace EvaluationSystem.XLAction
{
public class IPCacheInfoModel
{
/// <summary>
/// IP
/// </summary>
public string IP { get; set; } /// <summary>
/// 限制次数
/// </summary>
public int Limit { get; set; } /// <summary>
/// 是否可以访问
/// </summary>
public bool IsVisit { get; set; } /// <summary>
/// 访问时间
/// </summary>
private List<DateTime> reqTime = new List<DateTime>(); /// <summary>
/// 访问时间
/// </summary>
public List<DateTime> ReqTime
{
get { return this.reqTime; }
set { this.reqTime = value; }
}
}
}

时间按秒算
        private static int maxTimes ;
请求次数
        private static int partSecond ;

为了方便控制,不去修改我们的API程序,可以将这两个信息配置进appsettings.json文件里面

"XLAction": {//请求限流 秒钟一次
    "maxTimes": "1",
    "partSecond": "1"
  }

为了获取appsettings.json来买你的信息,我们需要一个方法拿到json里面的信息

GetConfiguration

    public class GetConfig
{
public static string GetConfiguration(string configKey)
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json");
var config = builder.Build();
if (configKey.Contains(":"))
{
return config.GetSection(configKey).Value;//获取分级参数值
}
else
{
return config[configKey];//获取直级参数值
}
//youdianwenti w xiangxiang
}
}

以上工作准备完全后,在我们的Startup里面修改加入以下代码

如果有ConfigureServices类,添加如下

//注册guolv
            services.AddControllers(o =>
            {
                o.Filters.Add<ApiAuthorize>();
                o.Filters.Add<MyAuthentication>();
                //o.Filters.Add(typeof(BasicAuthAttribute));
                //services.AddJwtEx();//这里就是注入JWT
            });

如果不是 如下添加

builder.Services.AddMvc(options => options.Filters.Add(new AuthorizeFilter()));

//注册guolv
builder.Services.AddControllers(o =>
{
    o.Filters.Add<ApiAuthorize>();
    o.Filters.Add<MyAuthentication>();
});

然后就大功告成

现在直接看结果

接着频繁操作

该方案来自网络加以修改,如有侵权,请联系删除

白嫖一个WebAPI限流解决方案的更多相关文章

  1. 白嫖一个月的ES,完成了与MySQL的联动

    前言 <腾讯云 x Elasticsearch三周年>活动来了.文章写之前的思路是:在腾讯云服务器使用docker搭建ES.但是理想很丰满,显示很骨感,在操作过程中一波三折,最后还是含着泪 ...

  2. webapi限流框架WebApiThrottle

    为了防止网站意外暴增的流量比如活动.秒杀.攻击等,导致整个系统瘫痪,在前后端接口服务处进行流量限制是非常有必要的.本篇主要介绍下Net限流框架WebApiThrottle的使用. WebApiThro ...

  3. 【分布式架构】--- 基于Redis组件的特性,实现一个分布式限流

    分布式---基于Redis进行接口IP限流 场景 为了防止我们的接口被人恶意访问,比如有人通过JMeter工具频繁访问我们的接口,导致接口响应变慢甚至崩溃,所以我们需要对一些特定的接口进行IP限流,即 ...

  4. 基于redis+lua实现高并发场景下的秒杀限流解决方案

    转自:https://blog.csdn.net/zzaric/article/details/80641786 应用场景如下: 公司内有多个业务系统,由于业务系统内有向用户发送消息的服务,所以通过统 ...

  5. 想学spark但是没有集群也没有数据?没关系,我来教你白嫖一个!

    本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是spark专题的第六篇文章,这篇文章会介绍一个免费的spark平台,我们可以基于这个平台做一些学习实验. databricks 今天要 ...

  6. 【Dnc.Api.Throttle】适用于.Net Core WebApi接口限流框架

    Dnc.Api.Throttle    适用于Dot Net Core的WebApi接口限流框架 使用Dnc.Api.Throttle可以使您轻松实现WebApi接口的限流管理.Dnc.Api.Thr ...

  7. 【.NET Core项目实战-统一认证平台】第七章 网关篇-自定义客户端限流

    [.NET Core项目实战-统一认证平台]开篇及目录索引 上篇文章我介绍了如何在网关上增加自定义客户端授权功能,从设计到编码实现,一步一步详细讲解,相信大家也掌握了自定义中间件的开发技巧了,本篇我们 ...

  8. 【Distributed】限流技巧

    一.概述 1.1 高并发服务限流特技 1.2 为什么要互联网项目要限流 1.3 高并发限流解决方案 二.限流算法 2.1 计数器 2.2 滑动窗口计数 2.3 令牌桶算法 使用RateLimiter实 ...

  9. 基于kubernetes的分布式限流

    做为一个数据上报系统,随着接入量越来越大,由于 API 接口无法控制调用方的行为,因此当遇到瞬时请求量激增时,会导致接口占用过多服务器资源,使得其他请求响应速度降低或是超时,更有甚者可能导致服务器宕机 ...

  10. 详解Redisson分布式限流的实现原理

    摘要:本文将详细介绍下RRateLimiter的具体使用方式.实现原理还有一些注意事项. 本文分享自华为云社区<详解Redisson分布式限流的实现原理>,作者: xindoo. 我们目前 ...

随机推荐

  1. 帝国cms 批量删除或者清空classurl(二级域名绑定)

      update phome_enewsclass set classurl= null ;

  2. 部署kubernetes-dashboard并配置ServiceAccount和登录鉴权

    "种草" kubernetes-dashboard 安装部署dashboard 创建用于登录面板的ServiceAccount 权限控制 "种草" kubern ...

  3. 五天学会Deep Learning

    五天学完deep learning......是时候来证明chatGPT和new bing的能力了...... DAY1 Sigmoid function Sigmoid 函数是一种常用的激活函数,它 ...

  4. This application failed to start because it could not find or load the Qt platforms plugins

     由于一直在linux下操作,今天Qt移植平台的时候导致.exe可执行文件一直运行不起来,提示缺少某些dll库,这个问题解决起来简单(直接去qt源码里面查找对应库添加到可执行文件目录就行),但是之后一 ...

  5. Python_14 接口测试报告

    一.查缺补漏 1. 测试用例要复制到pycharm执行的项目中,才能显示 2. 函数用下划线,类用大驼峰 3. pycharm一行显示(不换行): File -> settings-> E ...

  6. C# 手写识别方案整理

    书写识别,网上的大佬们都有输出. 书写识别存在的2个问题: 直接拿官网的案例(将 Windows Ink 笔划识别为文本和形状 - Windows apps | Microsoft Learn),会发 ...

  7. 【性能】JDK和Jmeter的安装与配置

    一.JDK环境配置 1. 下载JDK 官网下载地址:http://www.oracle.com/technetwork/java/javase/downloads/java-archive-downl ...

  8. Prism Sample 8 ViewModelLocator

    这一例只是说明ViewModelLocator的使用,没有难度,跳过.

  9. Layui 2.8.0 正式发布,官网全新文档站朴实归来

    前言 两年前 Layui 官网宣布了下线声明,说实话当时内心确实感慨万千毕竟这个免费为我们后端程序员提供的一个前端快熟开发框架的官网就这样下线了确实十分的惋惜,但是庆幸的是官网的下线,只是单纯一个网站 ...

  10. 百度飞桨(PaddlePaddle)- 张量(Tensor)

    飞桨 使用张量(Tensor) 来表示神经网络中传递的数据,Tensor 可以理解为多维数组,类似于 Numpy 数组(ndarray) 的概念.与 Numpy 数组相比,Tensor 除了支持运行在 ...