AspNetCore添加API限流
最近发现有客户在大量的请求我们的接口,出于性能考虑遂添加了请求频率限制。
由于我们接口请求的是.Net Core写的API网关,所以可以直接添加一个中间件,中间件中使用请求的地址当key,通过配置中心读取对应的请求频率参数设置,然后通过设置redis的过期时间就能实现了。
添加一个中间件ApiThrottleMiddleware,使用httpContext.Request.Path获取请求的接口,然后以次为key去读取配置中心设置的请求频率设置。(Ps:使用_configuration.GetSection(apiUrl).Get<ApiThrottleConfig>()不知为何返回值为null,这个还在查)
public class ApiThrottleMiddleware
{
private readonly RequestDelegate _next;
private IConfiguration _configuration;
private readonly IRedisRunConfigDatabaseProvider _redisRunConfigDatabaseProvider;
private readonly IDatabase _database; public ApiThrottleMiddleware(RequestDelegate next,
IConfiguration configuration,
IRedisRunConfigDatabaseProvider redisRunConfigDatabaseProvider)
{
_next = next;
_configuration = configuration;
_redisRunConfigDatabaseProvider = redisRunConfigDatabaseProvider;
_database = _redisRunConfigDatabaseProvider.GetDatabase();
} public async Task Invoke(HttpContext httpContext)
{
var middlewareContext = httpContext.GetOrCreateMiddlewareContext();
var apiUrl = httpContext.Request.Path.ToString(); var jsonValue= _configuration.GetSection(apiUrl).Value;
var apiThrottleConfig=JsonConvert.DeserializeObject<ApiThrottleConfig>(jsonValue);
//var apiThrottleConfig = _configuration.GetSection(apiUrl).Get<ApiThrottleConfig>(); await _next.Invoke(httpContext);
}
}
我们使用的配置中心是Apollo,设置的格式如下,其中Duration为请求间隔/秒,Limit为调用次数。(下图设置为每分钟允许请求10次)
(Ps: 由于在API限流中间件前我们已经通过了一个接口签名验证的中间件了,所以我们可以拿到调用客户的具体信息)
如果请求地址没有配置请求频率控制,则直接跳过。否则先通过SortedSetLengthAsync获取对应key的记录数,其中key我们使用了 $"{客户Id}:{插件编码}:{请求地址}",以此来限制每个客户,每个插件对应的某个接口来控制请求频率。获取key对应集合,当前时间-配置的时间段到当前时间的记录。
1 /// <summary>
2 /// 获取key
3 /// </summary>
4 /// <param name="signInfo"></param>
5 /// <param name="apiUrl">接口地址</param>
6 /// <returns></returns>
7 private string GetApiRecordKey(InterfaceSignInfo signInfo,string apiUrl)
8 {
9 var key = $"{signInfo.LicNo}:{signInfo.PluginCode}:{apiUrl}";
10 return key;
11 }
12
13 /// <summary>
14 /// 获取接口调用次数
15 /// </summary>
16 /// <param name="signInfo"></param>
17 /// <param name="apiUrl">接口地址</param>
18 /// <param name="duration">超时时间</param>
19 /// <returns></returns>
20 public async Task<long> GetApiRecordCountAsync(InterfaceSignInfo signInfo, string apiUrl, int duration)
21 {
22 var key = GetApiRecordKey(signInfo, apiUrl);
23 var nowTicks = DateTime.Now.Ticks;
24 return await _database.SortedSetLengthAsync(key, nowTicks - TimeSpan.FromSeconds(duration).Ticks, nowTicks);
25 }
如果请求次数大于等于我们设置的频率就直接返回接口调用频率超过限制错误,否则则在key对应的集合中添加一条记录,同时将对应key的过期时间设置为我们配置的限制时间。
/// <summary>
/// 获取接口调用次数
/// </summary>
/// <param name="signInfo"></param>
/// <param name="apiUrl">接口地址</param>
/// <param name="duration">超时时间</param>
/// <returns></returns>
public async Task<long> GetApiRecordCountAsync(InterfaceSignInfo signInfo, string apiUrl, int duration)
{
var key = GetApiRecordKey(signInfo, apiUrl);
var nowTicks = DateTime.Now.Ticks;
return await _database.SortedSetLengthAsync(key, nowTicks - TimeSpan.FromSeconds(duration).Ticks, nowTicks);
}
然后只需要在Startup中,在API签名验证中间件后调用我们这个API限流中间件就行了。
以下为完整的代码
1 using ApiGateway.Core.Configuration;
2 using ApiGateway.Core.Domain.Authentication;
3 using ApiGateway.Core.Domain.Configuration;
4 using ApiGateway.Core.Domain.Errors;
5 using Microsoft.AspNetCore.Http;
6 using Microsoft.Extensions.Configuration;
7 using Newtonsoft.Json;
8 using StackExchange.Redis;
9 using System;
10 using System.Threading.Tasks;
11
12 namespace ApiGateway.Core.Middleware.Api
13 {
14 /// <summary>
15 /// API限流中间件
16 /// </summary>
17 public class ApiThrottleMiddleware
18 {
19 private readonly RequestDelegate _next;
20 private IConfiguration _configuration;
21 private readonly IRedisRunConfigDatabaseProvider _redisRunConfigDatabaseProvider;
22 private readonly IDatabase _database;
23
24 public ApiThrottleMiddleware(RequestDelegate next,
25 IConfiguration configuration,
26 IRedisRunConfigDatabaseProvider redisRunConfigDatabaseProvider)
27 {
28 _next = next;
29 _configuration = configuration;
30 _redisRunConfigDatabaseProvider = redisRunConfigDatabaseProvider;
31 _database = _redisRunConfigDatabaseProvider.GetDatabase();
32 }
33
34 public async Task Invoke(HttpContext httpContext)
35 {
36 var middlewareContext = httpContext.GetOrCreateMiddlewareContext();
37 var apiUrl = httpContext.Request.Path.ToString();
38
39 var jsonValue= _configuration.GetSection(apiUrl).Value;
40 var apiThrottleConfig=JsonConvert.DeserializeObject<ApiThrottleConfig>(jsonValue);
41 //var apiThrottleConfig = _configuration.GetSection(apiUrl).Get<ApiThrottleConfig>();
42 if (apiThrottleConfig!=null)
43 {
44 var count = await GetApiRecordCountAsync(middlewareContext.InterfaceSignInfo, apiUrl, apiThrottleConfig.Duration);
45 if (count >= apiThrottleConfig.Limit)
46 {
47 middlewareContext.Errors.Add(new Error("接口调用频率超过限制", GatewayErrorCode.OverThrottleError));
48 }
49 else
50 {
51 await AddApiRecordCountAsync(middlewareContext.InterfaceSignInfo, apiUrl, apiThrottleConfig.Duration);
52 }
53 }
54
55 await _next.Invoke(httpContext);
56 }
57
58 /// <summary>
59 /// 获取接口调用次数
60 /// </summary>
61 /// <param name="signInfo"></param>
62 /// <param name="apiUrl">接口地址</param>
63 /// <param name="duration">超时时间</param>
64 /// <returns></returns>
65 public async Task<long> GetApiRecordCountAsync(InterfaceSignInfo signInfo, string apiUrl, int duration)
66 {
67 var key = GetApiRecordKey(signInfo, apiUrl);
68 var nowTicks = DateTime.Now.Ticks;
69 return await _database.SortedSetLengthAsync(key, nowTicks - TimeSpan.FromSeconds(duration).Ticks, nowTicks);
70 }
71
72 /// <summary>
73 /// 添加调用次数
74 /// </summary>
75 /// <param name="signInfo"></param>
76 /// <param name="apiUrl">接口地址</param>
77 /// <param name="duration">超时时间</param>
78 /// <returns></returns>
79 public async Task AddApiRecordCountAsync(InterfaceSignInfo signInfo, string apiUrl, int duration)
80 {
81 var key = GetApiRecordKey(signInfo, apiUrl);
82 var nowTicks = DateTime.Now.Ticks;
83 await _database.SortedSetAddAsync(key, nowTicks.ToString(), nowTicks);
84 await _database.KeyExpireAsync(key, TimeSpan.FromSeconds(duration));
85 }
86
87 /// <summary>
88 /// 获取key
89 /// </summary>
90 /// <param name="signInfo"></param>
91 /// <param name="apiUrl">接口地址</param>
92 /// <returns></returns>
93 private string GetApiRecordKey(InterfaceSignInfo signInfo,string apiUrl)
94 {
95 var key = $"{signInfo.LicNo}:{signInfo.PluginCode}:{apiUrl}";
96 return key;
97 }
98 }
99 }
AspNetCore添加API限流的更多相关文章
- 服务接口API限流 Rate Limit 续
一.前言 上一篇文章中粗浅的介绍使用Redis和基于令牌桶算法进行对服务接口API限流,本文介绍另一种算法---漏桶算法的应用.Nginx想必大家都有所了解是一个高性能的 HTTP 和反向代理服务器, ...
- Springboot中使用redis进行api限流
api限流的场景 限流的需求出现在许多常见的场景中 秒杀活动,有人使用软件恶意刷单抢货,需要限流防止机器参与活动 某api被各式各样系统广泛调用,严重消耗网络.内存等资源,需要合理限流 淘宝获取ip所 ...
- 服务接口API限流 Rate Limit
一.场景描述 很多做服务接口的人或多或少的遇到这样的场景,由于业务应用系统的负载能力有限,为了防止非预期的请求对系统压力过大而拖垮业务应用系统. 也就是面对大流量时,如何进行流量控制? 服务接口的流量 ...
- java 服务接口API限流 Rate Limit
一.场景描述 很多做服务接口的人或多或少的遇到这样的场景,由于业务应用系统的负载能力有限,为了防止非预期的请求对系统压力过大而拖垮业务应用系统. 也就是面对大流量时,如何进行流量控制? 服务接口的流量 ...
- 两个简单的API限流实现方案
1, Ngnix限流 Nginx在架构中起到请求转发与负载均衡器的作用.外部req首先到Nginx监听的80端口,然后Nginx将req交给到监听8080端口的APP服务器处理.处理结果再经由Ngin ...
- Guava RateLimiter实现接口API限流
一.简介 Guava提供的RateLimiter可以限制物理或逻辑资源的被访问速率.RateLimit二的原理类似与令牌桶,它主要由许可发出的速率来定义,如果没有额外的配置,许可证将按每秒许可证规定的 ...
- 令牌桶算法实现API限流
令牌桶算法( Token Bucket )和 Leaky Bucket 效果一样但方向相反的算法,更加容易理解.随着时间流逝,系统会按恒定 1/QPS 时间间隔(如果 QPS=100 ,则间隔是 10 ...
- redis实现api限流
redis官方给出了参考文档:INCR 这里参考第一种方法,使用token bucket实现:每个用户每秒有一个Counter: func RateLimiter(uid string, rlType ...
- AspNetCore 限流中间件IpRateLimitMiddleware 介绍
IpRateLimitMiddleware(Github: AspNetCoreRateLimit) 是ASPNETCore的一个限流的中间件,用于控制客户端调用API的频次, 如果客户端频繁访问服务 ...
随机推荐
- Linux中系统时间同步ntpdate简介
Linux服务器运行久时,系统时间就会存在一定的误差,一般情况下可以使用date命令进行时间设置,但在做数据库集群分片等操作时对多台机器的时间差是有要求的,此时就需要使用ntpdate进行时间同步.所 ...
- vsftpd.conf配置文件详解
1.默认配置: 1>允许匿名用户和本地用户登陆. anonymous_enable=YES local_enable=YES 2>匿名用户使用的登陆名为ftp或anonymous,口令为空 ...
- 『心善渊』Selenium3.0基础 — 17、Selenium操作浏览器窗口的滚动条
目录 1.为什么操作滚动条 2.Selenium如何操作滚动条 3.Selenium操作滚动条方法 4.操作滚动条示例 5.下拉至聚焦元素的位置 (1)实现步骤: (2)实现示例: 1.为什么操作滚动 ...
- 关于使用Flex中图片处理
<?xml version="1.0" encoding="utf-8"?> <s:Application xmlns:fx="ht ...
- Docker+Vagrant+Gitlab 构建自动化的 CI/CD
如果你的开发流程是下面这个样子的, 那么你一定很好奇, 为什么我提交到仓库的代码可以自动部署并访问到最新的提交内容 这就是近年来兴起的 DevOps 文化, 很方便的解决了开发人员和运维人员每次发布版 ...
- Spring缓存的注解关键词解释
Spring缓存的注解关键词解释 @Cacheable支持缓存 @Cacheable可以标记在一个方法上,也可以标记在一个类上. 1.当标记在一个方法上时表示该方法是支持缓存的,当标记在一个类上时则表 ...
- java swagger ui 添加header请求头参数
我用到的swagger 主要有三款产品,swagger editor,swagger ui 和swagger codegen. swagger editor:主要是一个本地客户端,用来自己添加api, ...
- PHP利用百度ai实现文本和图片审核
之前做平台内容发布审核都是自己构建一套违禁词库,在代码中利用词库判断用户发布的内容,现在可以使用百度ai api完成这个功能.接下来就简单说下怎么做吧: 首先打开百度ai 开发平台 注册一个账号: 注 ...
- Java实验项目三——平面图形和立体图形抽象类
Program:按照下面要求完成类的设计 (1)设计一个平面图形抽象类和一个立体图形抽象类,提供该类对象公共的方法和属性. (2)修改项目三中第2题中所设计的球类.圆柱类,圆锥类.矩形类.三角形类.圆 ...
- 《PHP设计模式大全》系列分享专栏
<PHP设计模式大全>已整理成PDF文档,点击可直接下载至本地查阅https://www.webfalse.com/read/201739.html 文章 php设计模式介绍之编程惯用法第 ...