Найкращий спосіб реалізувати затримку запитів в ASP.NET MVC?


212

Ми експериментуємо з різними способами придушення дій користувачів протягом певного періоду часу :

  • Обмежте повідомлення / запитання
  • Обмежити зміни
  • Обмежте завантаження каналу

Наразі ми використовуємо Кеш, щоб просто вставити запис про активність користувача - якщо ця запис існує, якщо / коли користувач виконує ту саму діяльність, ми припиняємо.

Використання кешу автоматично дає нам застарілі очищення даних та розсувні вікна активності користувачів, але те, як його масштаб може бути проблемою.

Які ще існують способи забезпечення ефективного зменшення запитів / дій користувачів (акцент на стабільності)?


Ви намагаєтесь обмежити кожного користувача чи питання? Якщо на кожного користувача, можна використовувати сеанс, який би був меншим набором.
Грег Огл

1
Це на кожного користувача, але ми не могли використовувати сесію, оскільки для цього потрібні файли cookie - ми обмежуємося на основі IP-адреси на даний момент.
Джеррод Діксон

1
На сьогоднішній день розгляньте спеціальні пакети github.com/stefanprodan/MvcThrottle для сторінок MVC та github.com/stefanprodan/WebApiThrottle для веб-запитів api
Andy

Відповіді:


240

Ось загальна версія того, що ми використовували в Stack Overflow за останній рік:

/// <summary>
/// Decorates any MVC route that needs to have client requests limited by time.
/// </summary>
/// <remarks>
/// Uses the current System.Web.Caching.Cache to store each client request to the decorated route.
/// </remarks>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class ThrottleAttribute : ActionFilterAttribute
{
    /// <summary>
    /// A unique name for this Throttle.
    /// </summary>
    /// <remarks>
    /// We'll be inserting a Cache record based on this name and client IP, e.g. "Name-192.168.0.1"
    /// </remarks>
    public string Name { get; set; }

    /// <summary>
    /// The number of seconds clients must wait before executing this decorated route again.
    /// </summary>
    public int Seconds { get; set; }

    /// <summary>
    /// A text message that will be sent to the client upon throttling.  You can include the token {n} to
    /// show this.Seconds in the message, e.g. "Wait {n} seconds before trying again".
    /// </summary>
    public string Message { get; set; }

    public override void OnActionExecuting(ActionExecutingContext c)
    {
        var key = string.Concat(Name, "-", c.HttpContext.Request.UserHostAddress);
        var allowExecute = false;

        if (HttpRuntime.Cache[key] == null)
        {
            HttpRuntime.Cache.Add(key,
                true, // is this the smallest data we can have?
                null, // no dependencies
                DateTime.Now.AddSeconds(Seconds), // absolute expiration
                Cache.NoSlidingExpiration,
                CacheItemPriority.Low,
                null); // no callback

            allowExecute = true;
        }

        if (!allowExecute)
        {
            if (String.IsNullOrEmpty(Message))
                Message = "You may only perform this action every {n} seconds.";

            c.Result = new ContentResult { Content = Message.Replace("{n}", Seconds.ToString()) };
            // see 409 - http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
            c.HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict;
        }
    }
}

Використання зразка:

[Throttle(Name="TestThrottle", Message = "You must wait {n} seconds before accessing this url again.", Seconds = 5)]
public ActionResult TestThrottle()
{
    return Content("TestThrottle executed");
}

Кеш ASP.NET працює тут як шампіньон - використовуючи його, ви отримуєте автоматичне очищення записів дросельної заслінки. І при зростаючому трафіку ми не бачимо, що це проблема на сервері.

Не соромтеся давати відгуки про цей метод; коли ми покращимо Stack Overflow краще, ви отримаєте виправлення Ewok ще швидше :)


5
швидке запитання - ви використовуєте значення c.HttpContext.Request.UserHostAddress як частина ключа. Чи можливо це значення порожнім, нульовим чи все те саме значення? (тобто, якщо ви використовуєте балансир навантаження і це IP-адреса цієї машини. Не справжні клієнти). Так, робіть проксі-сервери або балансири навантаження (тобто BIG IP F5) вводите туди ті самі дані, і вам потрібно перевірити для X-Forwarded-For також або щось таке?
Pure.Krome

7
@ Pure.Krome - так, могло бути. Отримуючи IP-адресу клієнта, ми використовуємо допоміжну функцію, яка перевіряє REMOTE_ADDRі HTTP_X_FORWARDED_FORзмінні, і сервер, і санітарно очищає належним чином.
Jarrod Dixon

3
@BrettRobi, я впевнений, що вони мають спорідненість із сервером на основі IP-адреси користувачів. Таким чином, вони, ймовірно, все ще будуть потрапляти на той же сервер.
mmcdole

4
Для тих із вас, хто турбується та читав це далеко в потоці коментарів ... ми закінчили написання власних переспрямувань, які очищають ключ кеш-дроселя перед перенаправленням. Таким чином всі переадресації проходять через код для видалення ключа, і жоден з них не запускає атрибут Throttle.
SLoret

4
Якщо ви шукаєте версію веб-API цього, перевірте тут: stackoverflow.com/questions/20817300/…
Папа Бургундія,

68

Microsoft має нове розширення для IIS 7 під назвою Динамічне розширення обмежень IP для IIS 7.0 - Beta.

"Динамічні обмеження IP для IIS 7.0 - це модуль, що забезпечує захист відмови в обслуговуванні та жорстоких атак на веб-сервер та веб-сайти. Такий захист забезпечується тимчасовим блокуванням IP-адрес клієнтів HTTP, які роблять незвично велику кількість одночасних запитів або які надсилають велику кількість запитів протягом невеликого періоду часу ". http://learn.iis.net/page.aspx/548/using-dynamic-ip-restrictions/

Приклад:

Якщо встановити критерії після блокування X requests in Y millisecondsабо X concurrent connections in Y millisecondsIP-адресу буде заблоковано, Y millisecondsзапити будуть дозволені знову.


1
Чи знаєте ви, чи це спричинило проблеми з такими сканерами, як Googlebot?
Гелефант


1
Зараз випущено та постачається в комплекті з IIS версії 8 - iis.net/learn/get-started/whats-new-in-iis-8/…
Меттью

Я хотів би скористатися цим, але це НЕ дозволяє вам запускати <location>. Це кожен запит на додаток або жоден.
Спікер Кейсі

Це не здається корисним, якщо ваші веб-сервери знаходяться за балансиром навантаження, оскільки весь трафік виявиться з однієї і тієї ж IP-адреси. Якщо я не пропускаю чогось очевидного ...
Dscoduc

11

Ми використовуємо техніку, запозичену за цією URL-адресою http://www.codeproject.com/KB/aspnet/10ASPNetPerformance.aspx , не для дроселювання, а для відмови у бідній людині (DOS). Це також на основі кешу, і може бути схожим на те, що ви робите. Ви дроселюєте, щоб запобігти атакам DOS? Маршрутизатори, безумовно, можна використовувати для зменшення DOS; ви думаєте, що маршрутизатор міг би впоратися з потрібними дроселями?


1
Це майже все, що ми вже робимо - але це чудово працює :)
Jarrod Dixon
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.