在ASP.NET Web API中使用静态类



我用自动翻译发布这个问题。请原谅任何语法错误。

我使用.NET框架和ASP.NET Web API构建了一个应用程序。我在IIS上运行的站点中为每个客户区域拆分了虚拟路径,并复制了相同的二进制文件以作为单独的应用程序运行。应用程序在同一个应用程序池中运行。

最近,一些客户在几分钟内就提出了大量请求。(我怀疑客户端的系统出现故障)。我正在考虑在我当前的应用程序中添加一个静态类,它可以跟踪给定时间段内每个客户的请求数量,并在超过阈值时阻止它们。从过去的StackOverFlow文章中,我发现";如果应用程序池被回收则静态类中的信息丢失";,但我已经确定,在这种情况下这不是问题。出于我的目的,我只需要能够保留几分钟的信息。

然而,我仍然有一些问题找不到答案,所以我想问大家几个问题。

  1. 即使同一个二进制文件在同一个应用程序池中运行,静态类信息是否会针对不同的应用程序单独保存?

  2. 即使在应用程序池被回收后,静态类的静态构造函数也会被执行吗?

  3. 如果我从静态类中引用Global.asax中的字段,会有问题吗?

  4. 从静态类中引用web.config的内容是否有问题?

下面的附件是我的实验实现的来源。我计划将静态方法称为";ExcessiveRequestCheck.isExcessiveRequest";在Web API接收到请求并识别用户ID之后,该静态类的。

如有任何建议,我们将不胜感激。

p.S。我知道这种方法在负载平衡环境中不能很好地工作。目前,我的系统只在一台虚拟机上运行。如果您正在迁移到云或部署负载均衡器,您可能需要一种与此不同的方法。

public static class ExcessiveRequestCheck
{
private static Dictionary<string, ExcessiveRequestInfo> dicExcessiveRequestCheckInfo = new Dictionary<string, ExcessiveRequestInfo>();
private static object initLock = new object();
private static object dicExcessiveRequestCheckInfoLock = new object();
//If possible, I want this process to be a static constructor
public static Dictionary<int, int> dicExcessiveRequestSkipConditions
{
get
{
lock (initLock)
{
if (ExcessiveRequestCheck._dicExcessiveRequestSkipConditions == null)
{
//if possible, I want to set this value from Web.config.
ExcessiveRequestCheck._dicExcessiveRequestSkipConditions = new Dictionary<int, int>() {
{ 5, 3 }, { 15, 5 }, { 45, 10 }, { 120, 20 }
};
}
return ExcessiveRequestCheck._dicExcessiveRequestSkipConditions;
}
}
}
private static Dictionary<int, int> _dicExcessiveRequestSkipConditions = null;
public const int BUFFER_CLEAR_MINUTES = 5;
public static bool isExcessiveRequest(string userId)
{
ExcessiveRequestCheck.refreshExcessiveRequestCheckInfo();
lock (ExcessiveRequestCheck.dicExcessiveRequestCheckInfoLock)
{
if (ExcessiveRequestCheck.dicExcessiveRequestCheckInfo.ContainsKey(userId) == false)
{
ExcessiveRequestCheck.dicExcessiveRequestCheckInfo.Add(userId, new ExcessiveRequestInfo() { countRequest = 1 });
return false;
}
bool doSkip = false;
ExcessiveRequestCheck.dicExcessiveRequestCheckInfo[userId].countRequest++;
foreach (KeyValuePair<int, int> pair in ExcessiveRequestCheck.dicExcessiveRequestSkipConditions)
{
if (ExcessiveRequestCheck.dicExcessiveRequestCheckInfo[userId].lastRequesttTime.AddSeconds(pair.Key) > DateTime.Now)
{
if (ExcessiveRequestCheck.dicExcessiveRequestCheckInfo[userId].countRequest > pair.Value)
{
ExcessiveRequestCheck.dicExcessiveRequestCheckInfo[userId].wasRequestSkip = true;
doSkip = true;
}
}
}
ExcessiveRequestCheck.dicExcessiveRequestCheckInfo[userId].lastRequesttTime = DateTime.Now;
return doSkip;
}
}
public static void refreshExcessiveRequestCheckInfo()
{
lock (ExcessiveRequestCheck.dicExcessiveRequestCheckInfoLock)
{
var keyList = ExcessiveRequestCheck.dicExcessiveRequestCheckInfo.Keys;
foreach (string key in keyList)
{
if (ExcessiveRequestCheck.dicExcessiveRequestCheckInfo.ContainsKey(key))
{
var value = ExcessiveRequestCheck.dicExcessiveRequestCheckInfo[key];
if (value.lastRequesttTime.AddMinutes(BUFFER_CLEAR_MINUTES) < DateTime.Now)
{
if (value.wasRequestSkip)
{
//this NLog instance was created in Global.asax.cs
WebApiApplication.logger.Fatal("skip request! user id=" + key);
}
ExcessiveRequestCheck.dicExcessiveRequestCheckInfo.Remove(key);
}
}
}
}
}
}
class ExcessiveRequestInfo
{
public DateTime requestStartTime { get; set; } = DateTime.Now;
public DateTime lastRequesttTime { get; set; } = DateTime.Now;
public int countRequest { get; set; } = 0;
public bool wasRequestSkip { get; set; } = false;
}

您的问题

即使同一个二进制文件在同一个应用程序池中运行,静态类信息是否会针对不同的应用程序单独保存?

是的,它们是单独的

即使在应用程序池被回收后,静态类的静态构造函数也会被执行吗?

是的,保证在执行任何静态方法之前调用静态构造函数

如果我从静态类中引用Global.asax中的字段,会有问题吗?

只不过是从其他任何地方访问

从静态类中引用web.config的内容有问题吗?

只不过是从其他任何地方访问

您的总体方法

拒绝服务

如果你试图减轻拒绝服务攻击或凭据填充攻击,你的方法可能不会奏效,因为对你的服务的请求仍然会导致向你的服务器添加负载,如果他们正在执行凭据填充攻击时,它会在你的字典中填充数百万个条目,并可能导致你的应用程序崩溃。

如果你想有效地减轻拒绝服务攻击,你可能需要一个更面向网络的解决方案,比如智能防火墙或WAF。

速率限制

另一方面,如果你试图限制特定用户的活动(即速率限制),那么你的方法可能不是最好的,因为它不支持负载平衡——你的列表保存在进程内存中。对于每个用户的速率限制,您可能需要在所有服务器都可以访问的中央数据存储中跟踪用户活动。

静态构造函数

一般来说,您应该尽量避免使用静态构造函数,或者保持它们非常简单,因为静态构造函数的失败会导致整个应用程序无法启动。小心!

即使同一个二进制文件在同一个应用程序池中运行,静态类信息是否会针对不同的应用程序单独保存?

如果是不同的应用程序,你指的是不同的网站?是的,它将与你为该应用程序池运行的每个网站分开。

即使在应用程序池被回收后,静态类的静态构造函数也会被执行吗?

哼,这有点令人困惑。只有当您调用类和给定的构造函数时,才会执行构造函数。由于从来没有创建类的实例;初始化/新建";事件从未被使用或触发。因此,任何带参数的方法都可以运行并正常工作,包括构造函数。我建议;事件";它在第一次使用时被触发——在静态类的上下文中它没有意义,也没有意义,因为你从来没有创建过实例。所以,如果你有一些带参数的方法,那就好了。所以,构造函数在类的新实例的上下文中是没有意义的——(甚至不认为这在static中是可能的)。不存在";新的";事件触发,所以我不明白这个问题会有多重要。

如果我从静态类中引用Global.asax中的字段,会有问题吗?

好吧,该类中的值对所有用户都是全局的。但是,这些值可能会超出范围,只要你愿意。因此,零使用公众成员已成为惯例。当应用程序池重新启动时,会重新设置这些类值吗?它们几乎在任何时候都可能超出范围。它们对所有用户都是全球性的。因此,对于生产代码来说,持久化值或试图在静态类中持久化值是不可行的选择。该类中可以有方法(代码),但任何公共的持久化值都不能正确地持久化。我不能百分之百确定,但即使只是普通的.net垃圾收集也可能导致重新设置。

如果您需要这些信息来持久化,那么您就不能使用static,您必须创建该类的实例并将其持久化在session()中。会话是每个用户的。静态类的公共值将应用于每个用户,而不仅仅是当前用户。事实上,这些价值观对所有用户来说都是全球性的,但如果没有任何真正的控制或对这些价值观将持续存在的担忧,你就无法控制这一点,因此你无法采用任何实践价值体系的结论和设计。

从静态类中引用web.config的内容有问题吗?

正在读取值?没问题。更新或修改值?-一个完全不同的问题。您修改了web.config,这将触发应用程序池重新启动。

因此,您可以自由读取任何文件-文本文件,xml或其他任何文件,其中包括web.config。只要您不修改这些文件,就没有问题。

这里的主要问题是什么?

假设或构建一个公共静态类值要持久存在的设计是不现实的。当这些值可能超出范围时,你所拥有的ZERO控制权是你可以控制的,因此这些设计不能使用也不能依赖于值的持久性。

当然,在许多网络托管系统上呢?他们现在正在采用云计算。这意味着,从一篇文章到下一篇文章,你可能使用不同的服务器,同样,这意味着这样的值不能在内存中持久存在,因为从一篇帖子到下一个帖子,或者从一个web服务调用到下一次?无论如何,您很可能正在访问不同的服务器(而且它们不共享内存)(因此,这建议使用基于SQL服务器的会话,或者至少在数据库中持久化这些值)。

事实上,如果你需要这样持久的价值观和数据?然后使用数据库。基于网络的软件的整个想法是你没有状态之间的帖子。你正试图走上一条更糟糕的路,但希望在翅膀上,祈祷一些全球价值观;可能";以及";有点";以及";也许";将在对网站的调用之间持续存在。答案:你真的不能用任何可靠的领域来做到这一点。

所以,你的大部分问题其实并不重要。重要的是这些价值观要坚持下去,你不能依赖这样的设计。如果您需要一些持久值,那么您必须采用支持该概念的系统和设计(viewstate、cookie或session())。

现在,我想你可以尝试一下,然后回来写一份详细的报告,以及你的经历。但是,有太多的陷阱,如果没有任何代码或系统控制内存中的持久值,我想我不会走上这条路。

在web领域,将公共变量保存在静态类中几乎毫无意义。你可以有代码,你可以有很酷的方法,你可以使用session()。但是,在静态类中持久化值的概念是一种没有意义的设计选择,也是不可依赖的。

Web软件被认为是无状态的,这在很大程度上是关于静态类的假设,或者实际上是对此类代码的一般使用。

最新更新