线程池友好的睡眠方法?

时间:2016-07-29 15:23:43

标签: asp.net .net iis httpmodule

我想在ASP.net应用程序中插入一个睡眠(又名油门,延迟,停机坪,停留)(想象失败的登录尝试升级延迟)。

protected void Page_Load(object sender, EventArgs e)
{
    Int32 sleepyTime = GetSleepyTime(Request);

    if (sleepyTime > 0)
        System.Threading.Thread.Sleep(sleepyTime);


    //...Continue normal processing
}

我希望所有剩余的处理能够正常继续;我只是想让用户代理受苦。

问题是ASP.net使用ThreadPool来处理请求。如果我要Sleep 5,10,30秒,我会吃掉宝贵的有限资源。

我认为它需要像:

protected void Page_Load(object sender, EventArgs e)
{
    Int32 sleepyTime = GetSleepyTime(Request);

    if (sleepyTime > 0)
       ABetterKindOfSleep(sleepyTime);

    //...Continue normal processing
}

private void ABetterKindOfSleep(int milliseconds)
{
   await SleepAsync(milliseconds);
}

private async void SleepAsync(int milliseconds)
{
   System.Threading.Thread.Sleep(milliseconds);
}

但是从来没有编写任何异步/等待代码,也没有理解asyncawait所处的逻辑,或者为什么,或者甚至可以用它来运行异步代码:i不知道它是否可用于运行异步代码。

奖金阅读

3 个答案:

答案 0 :(得分:0)

async等效Thread.Sleepawait Task.Delay

if (sleepyTime > 0)
  await Task.Delay(sleepyTime);

请注意,这必须在async方法的上下文中使用,并且在可以使用async的位置存在限制(特别是在WebForms上)。有关详情,请参阅我的article on async ASP.NETofficial tutorial on async WebForms

答案 1 :(得分:0)

这很容易。

首先,您创建一个IHttpModule类:

class TarpitHttpModule : IHttpModule
{
}

然后您通过在web.config中注册它来让IIS知道此模块:

<configuration>
   <system.webServer>
      <modules runAllManagedModulesForAllRequests="true">
         <add name="Tarpit" type="TarpitHttpModule"/>

如果您是Cassini,请将其添加到:

<configuration>
   <system.web>
      <httpModules>
         <add name="Tarpit" type="TarpitHttpModule"/>

无论何时有HTTP请求进入,IIS都会调用您的.Init方法。在此处,您将使用以下方法注册异步事件处理程序:

代码:

public void Init(HttpApplication application)
{
    //This is the synchronous event handler; which we don't want
    //application.BeginRequest += new EventHandler(this.Application_BeginRequest);

    //EventHandlerTaskAsyncHelper requires .NET 4.5
    //https://brockallen.com/2013/07/27/implementing-async-http-modules-in-asp-net-using-tpls-task-api/ 
    //  Archive: http://archive.is/Cdvle
    //
    //Normally you'd have to write a pair of methods:
    //    application.AddOnBeginRequestAsync(OnBegin, OnEnd);
    //
    //and then we'd have to write an OnBegin which returns IAsyncResult, and then OnEnd which takes the IAsyncResult.
    //The modern way is to use Tasks, and use the IAsyncResult that a Task **is**.
    //Fortunately the .NET team wrote a handy class that wraps up the boilerplate catching faults, etc,
    //and created the EventHandlerTaskAsyncHelper class

    var beginTaskHelper = new EventHandlerTaskAsyncHelper(BeginRequestAsync);
    application.AddOnBeginRequestAsync(beginTaskHelper.BeginEventHandler, beginTaskHelper.EndEventHandler);
}

所以现在我们必须提供 BeginRequestAsync 异步处理程序:

async Task BeginRequestAsync(object sender, EventArgs e)
{
    var application = (HttpApplication)sender;
    var context = application.Context;

    // In reality i would use the context.Request to come up with a unique key 
    // for this user agent e.g. 
    String key = SHA256(UserHostAddress+UserAgent+AcceptTypes+UserLanguages).ToBase64();
    // And use that as a cache key store information about this user agent
    Object tarpitInfo = context.Cache.Get(agentIdentity);
    if (ti == null)
        return;

    // But in this SO demo, i'm just going to unconditionally sleep
    Boolean waitPerformed = await PerformDelay(context, tarpitInfo);
    if (waitPerformed)
    {
        context.Response.StatusCode = 429;
        context.Response.StatusDescription = "Too Many Requests";
        context.Response.End();
        return;
    }
}

然后是睡觉的工作:

async Task<Boolean> PerformDelay(HttpContext context, TarInfo ti)
{
    int delayMs = 3000;
    Task delay = Task.Delay(delayMs);
    await delay;
    return true;
}

答案 2 :(得分:0)

我也想阻止机器人流量攻击登录端点,我担心如果我只是等待我将达到最大并发请求数或内存不足。我还没有找到使用Windows和Asp.Net的低开销方法。

我喜欢done here的方式,它改变了TCP / IP堆栈的行为以缩小窗口大小,而不是ACK后续数据包,这使得远程以指数方式退回,并且只发送少量的数据。

我可能会添加一些在前面运行HAProxy的Linux VM,以利用其DDOS capabilities