Asp.Net Core 7 preview 4 重磅新特性--限流

前言

限流是应对流量暴增或某些用户恶意攻击等场景的重要手段之一,然而微软官方从未支持这一重要特性,AspNetCoreRateLimit这一第三方库限流库一般作为首选使用,然而其配置参数过于繁多,对使用者造成较大的学习成本。令人高兴的是,在刚刚发布的.NET 7 Preview 4中开始支持限流中间件。

UseRateLimiter尝鲜

  1. 安装.NET 7.0 SDK(v7.0.100-preview.4)

  2. 通过nuget包安装Microsoft.AspNetCore.RateLimiting

  3. 创建.Net7网站应用,注册中间件

全局限流并发1个

app.UseRateLimiter(new RateLimiterOptions
{Limiter = PartitionedRateLimiter.Create<HttpContext, string>(resource =>{return RateLimitPartition.CreateConcurrencyLimiter("MyLimiter",_ => new ConcurrencyLimiterOptions(1, QueueProcessingOrder.NewestFirst, 1));})
});

根据不同资源不同限制并发数/api前缀的资源租约数2,等待队列长度为2,其他默认租约数1,队列长度1。

app.UseRateLimiter(new RateLimiterOptions()
{// 触发限流的响应码DefaultRejectionStatusCode = 500,OnRejected = async (ctx, rateLimitLease) =>{// 触发限流回调处理},Limiter = PartitionedRateLimiter.Create<HttpContext, string>(resource =>{if (resource.Request.Path.StartsWithSegments("/api")){return RateLimitPartition.CreateConcurrencyLimiter("WebApiLimiter",_ => new ConcurrencyLimiterOptions(2, QueueProcessingOrder.NewestFirst, 2));}else{return RateLimitPartition.CreateConcurrencyLimiter("DefaultLimiter",_ => new ConcurrencyLimiterOptions(1, QueueProcessingOrder.NewestFirst, 1));}})
});

本地测试

  1. 新建一个webapi项目,并注册限流中间件如下

using Microsoft.AspNetCore.RateLimiting;
using System.Threading.RateLimiting;var builder = WebApplication.CreateBuilder(args);// Add services to the container.builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();var app = builder.Build();// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{app.UseSwagger();app.UseSwaggerUI();
}app.UseRateLimiter(new RateLimiterOptions
{DefaultRejectionStatusCode = 500,OnRejected = async (ctx, lease) =>{await Task.FromResult(ctx.Response.WriteAsync("ConcurrencyLimiter"));},Limiter = PartitionedRateLimiter.Create<HttpContext, string>(resource =>{return RateLimitPartition.CreateConcurrencyLimiter("MyLimiter",_ => new ConcurrencyLimiterOptions(1, QueueProcessingOrder.NewestFirst, 1));})
});app.UseHttpsRedirection();app.UseAuthorization();app.MapControllers();app.Run();
  1. 启动项目,使用jmeter测试100并发,请求接口/WeatherForecast2ecd09acbd388bed8a37c48ff759ec07.png所有请求处理成功,失败0!

这个结果是不是有点失望,其实RateLimitPartition.CreateConcurrencyLimiter创建的限流器是ConcurrencyLimiter,后续可以实现各种策略的限流器进行替换之。看了ConcurrencyLimiter的实现,其实就是令牌桶的限流思想,上面配置的new ConcurrencyLimiterOptions(1, QueueProcessingOrder.NewestFirst, 1)),第一个1代表令牌的个数,第二个1代表可以当桶里的令牌为空时,进入等待队列,而不是直接失败,当前面的请求结束后,会归还令牌,此时等待的请求就可以拿到令牌了,QueueProcessingOrder.NewestFirst代表最新的请求优先获取令牌,也就是获取令牌时非公平的,还有另一个枚举值QueueProcessingOrder.OldestFirst老的优先,获取令牌是公平的。只要我们获取到令牌的人干活速度快,虽然我们令牌只有1,并发就很高。3. 测试触发失败场景 只需要让我们拿到令牌的人持有时间长点,就能轻易的触发。bb2be3c0ad9a7d1cfca442f1cb740751.png调整jmater并发数为1005ecfb63bb3efce61ef1c3fc09ed6ede.png

相应内容也是我们设置的内容。56e2534f62a3f5d475fae3891612a4a4.png

ConcurrencyLimiter源码

令牌桶限流思想

获取令牌

protected override RateLimitLease AcquireCore(int permitCount){// These amounts of resources can never be acquiredif (permitCount > _options.PermitLimit){throw new ArgumentOutOfRangeException(nameof(permitCount), permitCount, SR.Format(SR.PermitLimitExceeded, permitCount, _options.PermitLimit));}ThrowIfDisposed();// Return SuccessfulLease or FailedLease to indicate limiter stateif (permitCount == 0){return _permitCount > 0 ? SuccessfulLease : FailedLease;}// Perf: Check SemaphoreSlim implementation instead of lockingif (_permitCount >= permitCount){lock (Lock){if (TryLeaseUnsynchronized(permitCount, out RateLimitLease? lease)){return lease;}}}return FailedLease;}

尝试获取令牌核心逻辑

private bool TryLeaseUnsynchronized(int permitCount, [NotNullWhen(true)] out RateLimitLease? lease){ThrowIfDisposed();// if permitCount is 0 we want to queue it if there are no available permitsif (_permitCount >= permitCount && _permitCount != 0){if (permitCount == 0){// Edge case where the check before the lock showed 0 available permits but when we got the lock some permits were now availablelease = SuccessfulLease;return true;}// a. if there are no items queued we can lease// b. if there are items queued but the processing order is newest first, then we can lease the incoming request since it is the newestif (_queueCount == 0 || (_queueCount > 0 && _options.QueueProcessingOrder == QueueProcessingOrder.NewestFirst)){_idleSince = null;_permitCount -= permitCount;Debug.Assert(_permitCount >= 0);lease = new ConcurrencyLease(true, this, permitCount);return true;}}lease = null;return false;}

令牌获取失败后进入等待队列

protected override ValueTask<RateLimitLease> WaitAsyncCore(int permitCount, CancellationToken cancellationToken = default){// These amounts of resources can never be acquiredif (permitCount > _options.PermitLimit){throw new ArgumentOutOfRangeException(nameof(permitCount), permitCount, SR.Format(SR.PermitLimitExceeded, permitCount, _options.PermitLimit));}// Return SuccessfulLease if requestedCount is 0 and resources are availableif (permitCount == 0 && _permitCount > 0 && !_disposed){return new ValueTask<RateLimitLease>(SuccessfulLease);}// Perf: Check SemaphoreSlim implementation instead of lockinglock (Lock){if (TryLeaseUnsynchronized(permitCount, out RateLimitLease? lease)){return new ValueTask<RateLimitLease>(lease);}// Avoid integer overflow by using subtraction instead of additionDebug.Assert(_options.QueueLimit >= _queueCount);if (_options.QueueLimit - _queueCount < permitCount){if (_options.QueueProcessingOrder == QueueProcessingOrder.NewestFirst && permitCount <= _options.QueueLimit){// remove oldest items from queue until there is space for the newest requestdo{RequestRegistration oldestRequest = _queue.DequeueHead();_queueCount -= oldestRequest.Count;Debug.Assert(_queueCount >= 0);if (!oldestRequest.Tcs.TrySetResult(FailedLease)){// Updating queue count is handled by the cancellation code_queueCount += oldestRequest.Count;}}while (_options.QueueLimit - _queueCount < permitCount);}else{// Don't queue if queue limit reached and QueueProcessingOrder is OldestFirstreturn new ValueTask<RateLimitLease>(QueueLimitLease);}}CancelQueueState tcs = new CancelQueueState(permitCount, this, cancellationToken);CancellationTokenRegistration ctr = default;if (cancellationToken.CanBeCanceled){ctr = cancellationToken.Register(static obj =>{((CancelQueueState)obj!).TrySetCanceled();}, tcs);}RequestRegistration request = new RequestRegistration(permitCount, tcs, ctr);_queue.EnqueueTail(request);_queueCount += permitCount;Debug.Assert(_queueCount <= _options.QueueLimit);return new ValueTask<RateLimitLease>(request.Tcs.Task);}}

归还令牌

private void Release(int releaseCount){lock (Lock){if (_disposed){return;}_permitCount += releaseCount;Debug.Assert(_permitCount <= _options.PermitLimit);while (_queue.Count > 0){RequestRegistration nextPendingRequest =_options.QueueProcessingOrder == QueueProcessingOrder.OldestFirst? _queue.PeekHead(): _queue.PeekTail();if (_permitCount >= nextPendingRequest.Count){nextPendingRequest =_options.QueueProcessingOrder == QueueProcessingOrder.OldestFirst? _queue.DequeueHead(): _queue.DequeueTail();_permitCount -= nextPendingRequest.Count;_queueCount -= nextPendingRequest.Count;Debug.Assert(_permitCount >= 0);ConcurrencyLease lease = nextPendingRequest.Count == 0 ? SuccessfulLease : new ConcurrencyLease(true, this, nextPendingRequest.Count);// Check if request was canceledif (!nextPendingRequest.Tcs.TrySetResult(lease)){// Queued item was canceled so add count back_permitCount += nextPendingRequest.Count;// Updating queue count is handled by the cancellation code_queueCount += nextPendingRequest.Count;}nextPendingRequest.CancellationTokenRegistration.Dispose();Debug.Assert(_queueCount >= 0);}else{break;}}if (_permitCount == _options.PermitLimit){Debug.Assert(_idleSince is null);Debug.Assert(_queueCount == 0);_idleSince = Stopwatch.GetTimestamp();}}}

总结

虽然这次官方对限流进行了支持,但貌似还不能支持对ip或client级别的限制支持,对于更高级的限流策略仍需要借助第三方库或自己实现,期待后续越来越完善。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/287607.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【计算机图形学】实验:C#.net环境下采用GDI+图形特技处理案例教程

一、实验目的 掌握在.net环境下对图形特技的处理方法。 二、实验准备 学习在.net环境下读取图像文件、改变颜色等基本知识。 三、实验内容 在.net环境中实现对图像的灰度化处理、柔化和锐化、底片滤镜、淡入淡出以及浮雕、油画和木刻等艺术效果。 四、实验过程及步骤 (…

Windows之在终端打开当前目录的命令

1 问题 Windows在终端打开当前目录的命令 2 命令如下 1)、通过start命令 start . 2)、通过explorer命令 explorer .3 linux终端打开当前目录命令如下 nautilus . 4 mac终端打开当前目录命令如下 open .

孙钟秀-《 操作系统教程 》(第4版)注释(稿)

http://www.valleytalk.org/2014/12/30/%E5%AD%99%E9%92%9F%E7%A7%80-%E3%80%82%E3%80%8A-%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E6%95%99%E7%A8%8B-%E3%80%8B%EF%BC%88%E7%AC%AC4%E7%89%88%EF%BC%89%E6%B3%A8%E9%87%8A%EF%BC%88%E7%A8%BF/转载于:https://www.cnblogs.com/zen…

Android中build target,minSdkVersion,targetSdkVersion,maxSdkVersion概念区分

如果开发的应用用户较多&#xff0c;那么必须保证应用在多个版本不同的设备上能够正确的运行。这就要求对各个版本比较熟悉&#xff0c;知道在什么版本中加入了什么新的功能或特性。但是Android的版本太多了&#xff0c;是个令人头疼的问题。如果想了解Android的版本差异&#…

mysql交叉编译 cmake_CMake交叉编译配置

很多时候&#xff0c;我们在开发的时候是面对嵌入式平台&#xff0c;因此由于资源的限制需要用到相关的交叉编译。即在你host宿主机上要生成target目标机的程序。里面牵扯到相关头文件的切换和编译器的选择以及环境变量的改变等&#xff0c;我今天仅仅简单介绍下相关CMake在面对…

[python opencv 计算机视觉零基础到实战] 一 opencv的helloworld

前置条件 说明&#xff1a;本系列opencv实战教程将从基础到实战&#xff0c;若只是简单学习完python也可以通过该教程完成一般的机器学习编程&#xff1b;文中将会对很多python的基础内容进行讲解&#xff0c;但由于文章定位的原因将不会赘述过多的基础内容&#xff0c;基础内…

Android之用setColorFilter在ImageView上面覆盖一层灰色的图层

1 问题 在图片上面覆盖一层灰色的图层 2 解决办法 比如点击图片覆盖黑色 imageView.setColorFilter(ContextCompat.getColor(context, R.color.picture_color_black_80), PorterDuff.Mode.SRC_ATOP); 然后点击图片还原回来 imageView.setColorFilter(ContextCompat.getCol…

【计算机图形学】实验:VB.net环境下的综合绘图与交互技术案例教程

一、实验目的 掌握在.net环境下的绘图软件界面设计与交互技术。 二、实验准备 学习在.net环境下的界面设计的一般原理与交互技术等基本知识。 三、实验内容 将前7个实验内容集成到一个界面下&#xff0c;如直线段、圆、矩形与曲线的绘制填充&#xff0c;以及对图像的处理&…

IE8 兼容background-size的解决办法

.bgpic { background-image:url(); background-size:cover; filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src,sizingMethodscale); } 注意progid里面的src路径是绝对路径或是相对于页面的路径。​ div class"bgpic"style"width:200px;height:1…

从头开始敲代码之《从BaseApplication/Activity开始》

转载请注明出处王亟亟的大牛之路 其安易持&#xff0c;其未兆易谋&#xff1b;其脆易泮&#xff0c;其微易散。为之于未有&#xff0c;治之于未乱。合抱之木&#xff0c;生于毫末&#xff1b;九层之台&#xff0c;起于垒土&#xff1b;千里之行&#xff0c;始于足下。为者败之…

查缺补漏系统学习 EF Core 6 - 原始 SQL 查询

推荐关注「码侠江湖」加星标&#xff0c;时刻不忘江湖事这是 EF Core 系列的第五篇文章&#xff0c;上一篇文章盘点了 EF Core 中的几种数据查询方式。但是有有时候&#xff0c;我们可能无法用标准的 LINQ 方法完成查询任务。或者编译后的 LINQ 查询&#xff0c;没有我们想要的…

【python opencv 计算机视觉零基础到实战】二、 opencv文件格式与摄像头读取

一、学习目标 了解图片的结构属性了解如何捕获视频了解waitkey的使用方法 目录 [python opencv 计算机视觉零基础到实战] 一、opencv的helloworld [【python opencv 计算机视觉零基础到实战】二、 opencv文件格式与摄像头读取] 一、opencv的helloworld [[python opencv 计…

python冒泡排序代码完整_用Python写冒泡排序代码

python代码实现冒泡排序代码其实很简单&#xff0c;具体代码如下所示&#xff1a;代码Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--> 1 def bubbleSort(numbers):for j in xrange(len(numbers),-1,-1):for i in xra…

[C++]VS2005(VC8) 使用 Boost

測試環境&#xff1a;[1] Widnows XP Professional[2] Visual Studio 2005 Team Studio(VC8.0)[3] WinCvs 1.31. 下載 Boost透過 CVS 下載最新版 cvs -d:pserver:anonymousboost.cvs.sourceforge.net:/cvsroot/boost login [詢問密碼時&#xff0c;直接輸入 Enter 略過] cvs …

Android之编译提示error: Apostrophe not preceded by

1 问题 as编译提示错误如下 error: Apostrophe not preceded by 2 原因 字符串资源文件里面value包含一个单引号 <string name"key">Don t ....</string> 2 解决办法 1) 加双引号 <string name"key">"Don t ...."</s…

【ArcObject开发】实验:ArcGIS Desktop开发方式入门基础教程

一、实验目的: 熟练掌握ArcGIS Desktop开发方式。 二、实验准备: 学习ArcGIS Desktop定制步骤;熟悉VBA编程环境、Active DLL和Active EXE开发一般过程。 三、实验内容: (1)描述在ArcGIS Desktop环境下定制窗体界面的一般步骤;(2)在VBA环境下编写宏,实现图层视图…

C# WPF后台动态添加控件(经典)

概述在Winform中从后台添加控件相对比较容易&#xff0c;但是在WPF中&#xff0c;我们知道界面是通过XAML编写的&#xff0c;如何把后台写好的控件动态添加到前台呢&#xff1f;本节举例介绍这个问题。这里要用到UniformGrid布局&#xff0c;UniformGrid 是一种横向的网格分割、…

Android Button监听的方式

Android Button的几种监听方式 1、一个Button对应一个监听 1&#xff09;xml文件中绑定监听 <Buttonandroid:id"id/btn_test"android:layout_width"match_parent"android:layout_height"wrap_content"android:text"test listener"a…

hdu 5441 (并查集)

题意&#xff1a;给你n个点&#xff0c;m条边构成无向图。q个询问&#xff0c;每次一个值&#xff0c;求有多少条路&#xff0c;路中的边权都小于这个值 a->b 和 b->a算两种 思路&#xff1a;把权值从小到大排序&#xff0c;询问从小到大排序&#xff0c;如果相连则用并查…

【Envi风暴】Envi 5.4遥感影像镶嵌原来如此简单!

图像镶嵌指是在一定的数学基础控制下,把多景相邻的遥感图像拼接成一个大范围、无缝图像的过程。 Envi的图像镶嵌功能提供交互式的方式将没有地理坐标或者地理坐标的多幅图像合并,生成一幅单一的合成图像。镶嵌功能提供了透明处理、匀色、羽化等功能。 下面演示基于地理坐标(…