ASP.NET Core Web API 接口限流、限制接口并发数量,我也不知道自己写的有没有问题,抛砖引玉。
下面是使用jMeter并发测试时,打的接口日志:

接口参数的实体类要继承该接口
using JsonA = Newtonsoft.Json;
using JsonB = System.Text.Json.Serialization;
namespace Utils
{
/// <summary>
/// 限速接口
/// </summary>
public interface RateLimitInterface
{
/// <summary>
/// 是否限速
/// </summary>
[JsonA.JsonIgnore]
[JsonB.JsonIgnore]
bool IsLimit { get; }
}
}
继承RateLimitInterface接口,并实现IsLimit属性
public class XxxPostData : RateLimitInterface
{
...省略
/// <summary>
/// 是否限速
/// </summary>
[JsonA.JsonIgnore]
[JsonB.JsonIgnore]
public bool IsLimit
{
get
{
if (peoples.Count > 2) //限速条件,自己定义
{
return true;
}
return false;
}
}
}
作用:标签打在接口方法上,并设置并发数量
namespace Utils
{
/// <summary>
/// 接口限速
/// </summary>
public class RateLimitAttribute : Attribute
{
private Semaphore _sem;
public Semaphore Sem
{
get
{
return _sem;
}
}
public RateLimitAttribute(int limitCount = 1)
{
_sem = new Semaphore(limitCount, limitCount);
}
}
}
标签打在接口方法上,并设置并发数量。
服务器好像是24核的,并发限制为8应该没问题。
[HttpPost]
[Route("[action]")]
[RateLimit(8)]
public async Task<List<XxxInfo>> Query([FromBody] XxxPostData data)
{
...省略
}
/// <summary>
/// 接口限速
/// </summary>
public class RateLimitFilter : ActionFilterAttribute
{
public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
Type controllerType = context.Controller.GetType();
object arg = context.ActionArguments.Values.ToList()[0];
var rateLimit = context.ActionDescriptor.EndpointMetadata.OfType<RateLimitAttribute>().FirstOrDefault();
bool isLimit = false; //是否限速
if (rateLimit != null && arg is RateLimitInterface) //接口方法打了RateLimitAttribute标签并且参数实体类实现了RateLimitInterface接口时才限速,否则不限速
{
RateLimitInterface model = arg as RateLimitInterface;
if (model.IsLimit) //满足限速条件
{
isLimit = true;
Semaphore sem = rateLimit.Sem;
if (sem.WaitOne(0)) //注意:超时时间为0,表示不等待
{
try
{
await next.Invoke();
}
catch
{
throw;
}
finally
{
sem.Release();
}
}
else
{
var routeList = context.RouteData.Values.Values.ToList();
routeList.Reverse();
var route = string.Join('/', routeList.ConvertAll(a => a.ToString()));
var msg = $"当前访问{route}接口的用户数太多,请稍后再试";
LogUtil.Info(msg);
context.Result = new ObjectResult(new ApiResult
{
code = (int)HttpStatusCode.ServiceUnavailable,
message = "当前查询的用户太多,请稍后再试。"
});
}
}
}
if (!isLimit)
{
await next.Invoke();
}
}
}
上述代码说明:sem.WaitOne(0)这个超时时间,最好是0,即不等待,否则高并发下会有问题。SemaphoreSlim的异步wait没试过。如果超时时间大于0,意味着,高并发下,会有大量的等待,异步等待也是等待。
SemaphoreSlim短时间是自旋,想象一下一瞬间产生大量自旋会怎么样?所以最好不等待,如果要等待,那代码还得再研究研究,经过测试才能用。
//拦截器
builder.Services.AddMvc(options =>
{
...省略
options.Filters.Add<RateLimitFilter>();
});
测试结果:
测试三个接口,一个是触发限流的A接口,一个是未触发限流的A接口,一个是未被限流的B接口。
触发限流的A接口,并发量设置为200:

未触发限流的A接口以及未被限流的B接口,并发量设置为1:


截图说明:可以看到被限流接口共1000次调用,只有大约40次调用是成功的,剩下的返回请稍后再试。

截图说明:实际上触发限流的接口,并发量为8,压力依然很大,会拖慢自身以及其它接口,当触发限流的接口请求结束时,其它接口访问速度才正常。
代码如下:
/// <summary>
/// 接口限速
/// </summary>
public class RateLimitAttribute : Attribute
{
private SemaphoreSlim _sem;
public SemaphoreSlim Sem
{
get
{
return _sem;
}
}
/// <summary>
/// 超时时间(单位:毫秒)
/// </summary>
private int _timeout;
/// <summary>
/// 超时时间(单位:毫秒)
/// </summary>
public int Timeout
{
get
{
return _timeout;
}
}
/// <summary>
/// 接口限速
/// </summary>
/// <param name="limitCount">限制并发数量</param>
/// <param name="timeout">超时时间(单位:秒)</param>
public RateLimitAttribute(int limitCount = 1, int timeout = 0)
{
_sem = new SemaphoreSlim(limitCount, limitCount);
_timeout = timeout * 1000;
}
}
/// <summary>
/// 接口限速
/// </summary>
public class RateLimitFilter : ActionFilterAttribute
{
public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
Type controllerType = context.Controller.GetType();
object arg = context.ActionArguments.Values.ToList()[0];
var rateLimit = context.ActionDescriptor.EndpointMetadata.OfType<RateLimitAttribute>().FirstOrDefault();
bool isLimit = false;
if (rateLimit != null && arg is RateLimitInterface)
{
RateLimitInterface model = arg as RateLimitInterface;
if (model.IsLimit) //满足限速条件
{
isLimit = true;
SemaphoreSlim sem = rateLimit.Sem;
if (await sem.WaitAsync(rateLimit.Timeout))
{
try
{
await next.Invoke();
}
catch
{
throw;
}
finally
{
sem.Release();
}
}
else
{
var routeList = context.RouteData.Values.Values.ToList();
routeList.Reverse();
var route = string.Join('/', routeList.ConvertAll(a => a.ToString()));
var msg = $"当前访问{route}接口的用户数太多,请稍后再试";
LogUtil.Info(msg);
context.Result = new ObjectResult(new ApiResult
{
code = (int)HttpStatusCode.ServiceUnavailable,
message = "当前查询的用户太多,请稍后再试。"
});
}
}
}
if (!isLimit)
{
await next.Invoke();
}
}
}
1.postman介绍Postman一款非常流行的API调试工具。其实,开发人员用的更多。因为测试人员做接口测试会有更多选择,例如Jmeter、soapUI等。不过,对于开发过程中去调试接口,Postman确实足够的简单方便,而且功能强大。2.下载安装官网地址:https://www.postman.com/下载完成后双击安装吧,安装过程极其简单,无需任何操作3.使用教程这里以百度为例,工具使用简单,填写URL地址即可发送请求,在下方查看响应结果和响应状态码常用方法都有支持请求方法:getpostputdeleteGet、Post、Put与Delete的作用get:请求方法一般是用于数据查询,
require'mechanize'agent=Mechanize.newlogin=agent.get('http://www.schoolnet.ch/DE/HomeDE.htm')agent.clicklogin.link_withtext:/Login/然后我得到Mechanize::UnsupportedSchemeError。 最佳答案 Mechanize不支持javascript但您可以将搜索字段添加到表单并为其分配搜索词并使用mechanize提交表单form=page.forms.firstform.add_fie
我一直在为使用acts_as_list的模型实现一些不错的交互界面,这些界面可以对我的mRails应用程序中的列表进行排序。我有一个排序函数,在每次拖放之后使用sortable_elementscript.aculo.us函数调用并设置每条记录的位置。这是在拖放完成后处理排序的Controller操作示例:defsortparams[:documents].each_with_indexdo|id,index|Document.update_all(['position=?',index+1],['id=?',id])endend现在我正在尝试对嵌套集模型(acts_as_nested
最近在工作中,看到一些新手测试同学,对接口测试存在很多疑问,甚至包括一些从事软件测试3,5年的同学,在聊到接口时,也是一知半解;今天借着这个机会,对接口测试做个实战教学,顺便总结一下经验,分享给大家。计划拆分成4个模块跟大家做一个分享,(接口测试、接口基础知识、接口自动化、接口进阶)感兴趣的小伙伴记得关注,希望对你的日常工作和求职面试,带来一些帮助。注:文章较长有5000多字,希望小伙伴们认真看完,当然有些内容对小白同学不是太友好,如果你需要详细了解其中的一些概念或者名词,请在文章之后留言,后续我将针对大家的疑问,整理输出一些大家感兴趣的文章。随着开发模式的迭代更新,前后端分离已不是新的概念,
我正在尝试在我的Rails应用程序中使用模型来从外部API检索信息。我想做的是以类似于ActiveRecord模型提供的方式(特别是关联,以及相同风格的可链接查询方法)访问我的数据模型(可能包含来自多个API调用的信息)。我最初的直觉是重新创建我想要的ActiveRecord部分并合并此API。不想“重新发明轮子”并确切地看到添加更多功能需要多少工作让我退后一步并重新评估如何处理这个问题。我找到了在没有表的情况下使用ActiveRecord的方法(请参阅:Railscast#193TablelessModel和博客文章here)并研究了ActiveRecord。因为ActiveMode
原文:UsetheIComparableandIComparerinterfacesinVisualCSharp本文介绍了在VisualC#中如何使用IComparer和IComparable接口。概要本文同时讨论了IComparable和IComparer接口,原因有两点。这两个接口经常一起使用。虽然接口类似且名称相似,但它们却有不同的用途。如果你有一个支持IComparer的类型数组(例如字符串或整数),你可以对它进行排序而不需要提供任何对IComparer的显式引用(译注:意思是把一个IComparer的实现类作为参数传递给排序方法)。在这种情况下,数组元素会被转换为IComparer的
如果你想在自己的应用中使用淘宝的数据,那么对接淘宝数据接口是必不可少的一步。本文将介绍如何对接API淘宝数据接口,以便你能够顺利获取和使用淘宝的数据。步骤一:获取AppKey和AppSecret首先,在淘宝开放平台申请API接口之前,需要先注册为淘宝开发者并创建应用。创建应用后,你将得到一个AppKey和AppSecret,这两个参数需要在调用API接口时用于身份验证。步骤二:确定需要调用的API接口在淘宝开放平台的开发文档中,你将找到所有可用的API接口。你需要根据你的需求找到需要调用的API接口,例如查询商品、店铺等信息。在找到需要的API接口后,你需要了解该接口的请求参数和返回结
我正在使用delayed_job_webgem来监控延迟的作业。https://github.com/ejschmitt/delayed_job_web可以使用我的routes.rb中的这一行访问它:match"/delayed_job"=>DelayedJobWeb,:anchor=>false我站点的每个其他区域都需要使用Devisegem登录。我如何使它也需要登录?在自述文件中,他们建议将以下内容添加到config.rb:ifRails.env.production?DelayedJobWeb.useRack::Auth::Basicdo|username,password|us
我有一个大型用户数据库(约200,000个),我正在将其从ASP.NET应用程序转移到RubyonRails应用程序。我真的不想要求每个用户重置他们的密码,所以我试图在Ruby中重新实现C#密码哈希函数。旧函数是这样的:publicstringEncodePassword(stringpass,stringsaltBase64){byte[]bytes=Encoding.Unicode.GetBytes(pass);byte[]src=Convert.FromBase64String(saltBase64);byte[]dst=newbyte[src.Length+bytes.Leng
根据officialrubyAboutpage用C扩展Ruby比用Perl更容易。我不是(perl)XS专家,但我发现使用Inline::C快速简单地编写一些东西非常简单,那么为什么在Ruby中更容易呢?WritingCextensionsinRubyiseasierthaninPerlorPython,withaveryelegantAPIforcallingRubyfromC.ThisincludescallsforembeddingRubyinsoftware,foruseasascriptinglanguage.ASWIGinterfaceisalsoavailable.那些做