.NET服务治理之限流中间件-FireflySoft.RateLimit
概述
FireflySoft.RateLimit自2021年1月发布第一个版本以来,经历了多次升级迭代,目前已经十分稳定,被很多开发者应用到了生产系统中,最新发布的版本是3.0.0。
- Github:https://github.com/bosima/FireflySoft.RateLimit
- 码云:https://gitee.com/bosima/FireflySoft.RateLimit
它的核心是一个基于 .NET Standard 的限流类库,其内核简单轻巧,能够灵活应对各种需求的限流场景。其主要特点包括:
- 多种限流算法:内置固定窗口、滑动窗口、漏桶、令牌桶四种算法,方便自定义扩展。
- 多种计数存储:目前支持内存、Redis(含集群)两种存储方式。
- 分布式友好:通过Redis存储支持分布式程序统一计数。
- 限流目标灵活:可以从请求中提取各种数据用于设置限流目标,不仅仅是客户端IP和Id。
- 支持限流惩罚:可以在客户端触发限流后锁定一段时间不允许其访问。
- 时间窗口增强:支持到毫秒级别;支持从秒、分钟、小时、日期等时间周期的自然起始点开始。
- 实时限流跟踪:当前计数周期内已处理的请求数、剩余允许请求数,以及计数周期重置的时间。
- 动态更改规则:支持程序运行时动态更改限流规则。
- 自定义错误:可以自定义触发限流后的错误码和错误消息。
- 普适性:原则上可以满足任何需要限流的场景,可用于各种B/S、C/S程序。
基于这个核心又实现了两个中间件:
- FireflySoft.RateLimit.AspNet:应用于传统的.NET Framework的Web应用程序。
- FireflySoft.RateLimit.AspNetCore:应用于ASP.NET Core的Web应用程序。
相比使用FireflySoft.RateLimit核心类库,直接使用这两个中间件比较方便一些。如果这两个中间件不能满足你的需求,比如不是应用在官方的Web框架中,甚至不是Web程序,问题不大,可以基于核心类库满足你的限流需求,你要做的只是定义好你要限流的请求,并在触发限流时执行自己的业务逻辑,限流的算法如何实现都不需要关心。
这些类库和中间件都是可以通过Nuget安装的,搜索 FireflySoft.RateLimit 即可找到。
使用示例
这篇文章以一个ASP.NET Core程序为例,说明FireflySoft.RateLimit的使用方法。
程序的业务需求是:对获取天气预报的接口,根据客户端IP和ClientId进行限流,每个IP每秒钟1次,每个ClientId每秒钟3次。ClientId是预先分配给调用方的。根据规则,调用方如果只有1个出口IP,那么每秒钟只能访问1次,如果有多个出口IP,那么每秒钟最多访问3次。
这个示例程序是基于.NET6开发的,当然你用.NET Core 3.1也没有问题,只是.NET6默认把服务和中间件注册都放到了Program.cs 中。(建议升级到.NET6,.NET6相比.NET Core 3.1的性能有明显的提升。)
来看代码吧,只需要注册服务和Middleware就可以了。
using FireflySoft.RateLimit.AspNetCore;
using FireflySoft.RateLimit.Core.InProcessAlgorithm;
using FireflySoft.RateLimit.Core.Rule;
var builder = WebApplication.CreateBuilder(args);
...
builder.Services.AddRateLimit(new InProcessFixedWindowAlgorithm(
new[] {
new FixedWindowRule()
{
ExtractTarget = context =>
{
var httpContext= context as HttpContext;
// Through CDN
var ip = httpContext!.Request.Headers["Cdn-Src-Ip"].FirstOrDefault();
if (!string.IsNullOrEmpty(ip))
return ip;
// Through SLB
ip = httpContext!.Request.Headers["X-Forwarded-For"].FirstOrDefault();
if (!string.IsNullOrEmpty(ip))
return ip;
ip = httpContext!.Connection.RemoteIpAddress?.ToString();
return ip??"Anonymous-IP";
},
CheckRuleMatching = context =>
{
var requestPath = (context as HttpContext)!.Request.Path.Value;
if (requestPath == "/WeatherForecast/Future")
{
return true;
}
return false;
},
Name = "ClientIPRule",
LimitNumber = 3,
StatWindow = TimeSpan.FromSeconds(1)
},
new FixedWindowRule()
{
ExtractTarget = context =>
{
var httpContext= context as HttpContext;
var clientID = httpContext!.Request.Headers["X-ClientId"].FirstOrDefault();
return clientID??"Anonymous-ClientId";
},
CheckRuleMatching = context =>
{
var requestPath = (context as HttpContext)!.Request.Path.Value;
if (requestPath == "/WeatherForecast/Future")
{
return true;
}
return false;
},
Name = "ClientIdRule",
LimitNumber = 1,
StatWindow = TimeSpan.FromSeconds(1)
}
})
);
...
app.UseRateLimit();
...
粘贴的代码中只保留了此中间件需要的内容,注册服务使用 AddRateLimit,使用中间件通过 UseRateLimit。
算法
AddRateLimit 时需要指定一个限流算法,示例中是基于本地内存的固定窗口算法,可以根据需要更换为其它算法,比如可以应对短时突发流量的令牌桶算法。
对于某种具体的算法,基于本地内存和基于Redis的实现是不同的类,因为为了更好的性能,Redis实现的算法是通过Lua脚本写的,它完全运行在Redis服务端。
为了方便使用,将这些算法的名字列在这里:
| 基于本地内存(进程内) | 基于Redis | |
|---|---|---|
| 固定窗口算法 | InProcessFixedWindowAlgorithm | RedisFixedWindowAlgorithm |
| 滑动窗口算法 | InProcessSlidingWindowAlgorithm | RedisSlidingWindowAlgorithm |
| 漏桶算法 | InProcessFixedWindowAlgorithm | RedisFixedWindowAlgorithm |
| 令牌桶算法 | InProcessTokenBucketAlgorithm | RedisokenBucketAlgorithm |
目前一个ASP.NET Core程序中只能使用一种算法,不知道是否有多种算法的需求,如有需要可以对FireflySoft.RateLimit.AspNetCore 进行一些改造:
AddRateLimit时注册IAlgorithm改为注册IAlgorithm的解析器,解析器提供一个方法根据某个Key返回IAlgorithm的具体实现。
RateLimitMiddleware中根据当前请求确定要使用的算法,然后调用解析器的方法获取IAlgorithm的具体实现。
规则
创建算法实例的时候,还需要指定算法的规则,这里根据算法使用的是 FixedWindowRule,对于同一个算法,进程内实现和Redis实现使用相同的规则。
看一下这里使用的规则的几个属性:
ExtractTarget 设置一个函数,用于从HTTP请求中提取要限流的目标,比如这里的客户端IP和客户端ID,还可以是各种可以从请求中提取或关联到的东西,比如Http Header中携带的用户Id,或者根据用户Id查询到的用户年龄。
CheckRuleMatching 设置一个函数,返回当前请求是否能匹配到某个限流规则,如果能匹配到,则返回true。比如只对 /api/req 这个路径限流,那么只要判断请求的路径是它,就返回true,其它路径都返回false。当然也可以是根据各种可以从请求中提取或关联到的东西来进行判断。
Name 限流规则的名字,方便人跟踪的时候进行区分。
StatWindow 限流的时间窗口。比如需求中的每秒钟3次,这里的时间窗口就要设置为1秒。
LimitNumber 限流的次数阈值。比如需求中的每秒钟3次,这里的时间窗口就要设置为3,超过3就会被限流。
规则中还有其它几个属性,不同算法的规则也略有不同,这里就不一一列举了。感兴趣的朋友可以去示例代码和单元测试中认识它们。
一个算法中可以添加多个对应算法的规则,这无疑会比较灵活。
更多使用说明
- ASP.NET Core中使用固定窗口限流
- ASP.NET Core中使用滑动窗口限流
- ASP.NET Core中使用漏桶算法限流
- ASP.NET Core中使用令牌桶限流
- ASP.NET Core中如何对不同类型的用户进行区别限流
- 多租户系统中如何实现分别限流
- .NET6运行时动态更新限流阈值
- 如何使用数组实现滑动窗口
- 限流的非常规用途 – 缓解抢购压力
- 限流的非正式用途 – 解决重复提交问题
- 服务限流惩罚是怎么一回事
以上就是本文的主要内容了,如有问题欢迎留言交流。
.NET服务治理之限流中间件-FireflySoft.RateLimit的更多相关文章
- 服务接口API限流 Rate Limit 续
一.前言 上一篇文章中粗浅的介绍使用Redis和基于令牌桶算法进行对服务接口API限流,本文介绍另一种算法---漏桶算法的应用.Nginx想必大家都有所了解是一个高性能的 HTTP 和反向代理服务器, ...
- Hystrix介绍以及服务的降级限流熔断
(dubbo熔断,Hystrix问的少) 无论是缓存层还是存储层都会有出错的概率,可以将它们视同为资源.作为并发量较大的系统,假如有一个资源不可用,可能会造成线程全部 hang (挂起)在这个资源上, ...
- Asp.Net Core 7 preview 4 重磅新特性--限流中间件
前言 限流是应对流量暴增或某些用户恶意攻击等场景的重要手段之一,然而微软官方从未支持这一重要特性,AspNetCoreRateLimit这一第三方库限流库一般作为首选使用,然而其配置参数过于繁多,对使 ...
- ASP.NET Core WebApi AspNetCoreRateLimit 限流中间件学习
AspNetCoreRateLimit介绍: AspNetCoreRateLimit是ASP.NET核心速率限制框架,能够对WebApi,Mvc中控制限流,AspNetCoreRateLimit包包含 ...
- 从-99打造Sentinel高可用集群限流中间件
接上篇Sentinel集群限流探索,上次简单提到了集群限流的原理,然后用官方给的 demo 简单修改了一下,可以正常运行生效. 这一次需要更进一步,基于 Sentinel 实现内嵌式集群限流的高可用方 ...
- 服务接口API限流 Rate Limit
一.场景描述 很多做服务接口的人或多或少的遇到这样的场景,由于业务应用系统的负载能力有限,为了防止非预期的请求对系统压力过大而拖垮业务应用系统. 也就是面对大流量时,如何进行流量控制? 服务接口的流量 ...
- Golang微服务:Micro限流、熔断
Wrapper Wrapper提供了一种包装机制,使得在执行某方法前先执行Wrapper,优点Filter的意思:因此可以在客户端和服务器做很多功能:熔断限流.Filter.Auth等. client ...
- Laravel 限流中间件 throttle 简析
1. 在Laravel 中配置 在 app\Http\Kernel.php 中,默认添加到中间件组 api 下,1分钟60次. 2. 限流原理 获取唯一请求来源,进行唯一标识(key) 获取该请求请求 ...
- java 服务接口API限流 Rate Limit
一.场景描述 很多做服务接口的人或多或少的遇到这样的场景,由于业务应用系统的负载能力有限,为了防止非预期的请求对系统压力过大而拖垮业务应用系统. 也就是面对大流量时,如何进行流量控制? 服务接口的流量 ...
随机推荐
- Xray学习
Xray 目前支持的漏洞检测类型包括: XSS漏洞检测 (key: xss) SQL 注入检测 (key: sqldet) 命令/代码注入检测 (key: cmd-injection) 目录枚举 (k ...
- 关于python中selenium一些知识点
selenium几种元素操纵方法 切换iframe层 #切换至xx iframe层 driver.switch_to.frame("name and id") # 切回主HTML层 ...
- [洛谷] P1065 [NOIP2006 提高组] 作业调度方案
点击查看代码 #include<bits/stdc++.h> using namespace std; const int N = 1e6 + 10; int m, n, ans = 0; ...
- Springboot 整合 MyBatisPlus[详细过程]
Springboot 整合 MyBatisPlus[详细过程] 提要 这里已经将Springboot环境创建好 这里只是整合MyBatis过程 引入Maven依赖 添加MyBatisPlus启动依赖, ...
- 【Docker入门】Docker的常用命令
了解和安装完docker之后,我们学习一下docker的常用命令就和当初学linux命令一样,放心命令其实大致相同只不过细节不同. 一.Docker启动类命令 1.启动docker:syste ...
- css自定义省略实例1
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 虚拟机中CentOS-7.9的硬盘空间扩容(EXSI)
目录 一.增加虚机容量 二.创建新的分区 三.格式化新分区 四.lvm实现卷扩容 五.文件系统的扩容 大家好,我是LSF,发现一台虚机上 /dev/mapper/centos-root Use%已经快 ...
- 万字长文深度剖析 RocketMQ 设计原理
幸福的烦恼 张大胖最近是又喜又忧,喜的是业务量发展猛增,忧的是由于业务量猛增,一些原来不是问题的问题变成了大问题,比如说新会员注册吧,原来注册成功只要发个短信就行了,但随着业务的发展,现在注册成功也需 ...
- 【Java面试】Redis存在线程安全问题吗?为什么?
一个工作了5年的粉丝私信我. 他说自己准备了半年时间,想如蚂蚁金服,结果第一面就挂了,非常难过. 问题是: "Redis存在线程安全问题吗?" 关于这个问题,看看普通人和高手的回答 ...
- dd-文件系统内容映射
复制文件并对原文件的内容进行转换和格式化处理. 语法 dd [OPTIONS]... 选项 if=filename 指定源文件. of=filename 指定目的文件. ibs=bytes 指定一个块 ...