A melhor maneira de implementar a otimização de solicitações no ASP.NET MVC?

212

Estamos experimentando várias maneiras de controlar as ações do usuário em um determinado período :

  • Limitar postagens de perguntas / respostas
  • Limitar edições
  • Limitar recuperações de feed

Por enquanto, estamos usando o cache para simplesmente inserir um registro de atividade do usuário - se esse registro existir se / quando o usuário fizer a mesma atividade, aceleramos.

Usar o cache automaticamente nos fornece janelas obsoletas de limpeza e deslizamento de dados dos usuários, mas como será dimensionado pode ser um problema.

Quais são algumas outras maneiras de garantir que solicitações / ações do usuário possam ser efetivamente controladas (ênfase na estabilidade)?

Jarrod Dixon
fonte
Você está tentando limitar por usuário ou por pergunta? Se por usuário, poderia usar a sessão, que seria um conjunto menor.
Greg Ogle
1
É por usuário, mas não foi possível usar a Sessão, pois isso requer cookies - estamos limitando com base no endereço IP atualmente.
Jarrod Dixon
1
Atualmente, considere pacotes de nuget github.com/stefanprodan/MvcThrottle para páginas MVC e github.com/stefanprodan/WebApiThrottle para solicitações de API da Web
Andy

Respostas:

240

Aqui está uma versão genérica do que usamos no Stack Overflow no ano passado:

/// <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;
        }
    }
}

Uso da amostra:

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

O ASP.NET Cache funciona como um campeão aqui - ao usá-lo, você obtém a limpeza automática das entradas do acelerador. E com o nosso tráfego crescente, não estamos vendo que isso seja um problema no servidor.

Sinta-se livre para dar feedback sobre esse método; quando aprimoramos o Stack Overflow, você obtém o reparo do Ewok ainda mais rápido :)

Jarrod Dixon
fonte
5
pergunta rápida - você está usando o valor c.HttpContext.Request.UserHostAddress como parte da chave. Esse valor é possível vazio ou nulo ou o mesmo valor? (ou seja, se você estiver usando um balanceador de carga e for o IP dessa máquina ... não os clientes reais) Como os proxies ou balanceadores de carga (ou seja, um BIG IP F5), coloque os mesmos dados e você precisará verificar para X-Forwarded-For também ou algo assim?
Pure.Krome
7
@ Pure.Krome - sim, poderia ser. Ao recuperar o IP do cliente, usamos uma função auxiliar que verifica tanto o REMOTE_ADDRe HTTP_X_FORWARDED_FORvariáveis de servidor e sanitizes adequadamente.
Jarrod Dixon
3
@BrettRobi, tenho certeza de que eles têm afinidade com o servidor com base no endereço IP do usuário. Portanto, eles provavelmente ainda estarão atingindo o mesmo servidor.
mmcdole
4
Para aqueles de vocês que se importam e leram até aqui no fluxo de comentários ... acabamos escrevendo nossos próprios redirecionamentos que limpam a chave do cache do acelerador antes de redirecionar. Dessa forma, todos os redirecionamentos passam pelo código para remover a chave e nenhum deles aciona o atributo Throttle.
SLoret
4
Se você está procurando a versão Web API disso, confira aqui: stackoverflow.com/questions/20817300/...
Papa Burgundy
68

A Microsoft possui uma nova extensão para o IIS 7 chamada Dynamic IP Restrictions Extension para IIS 7.0 - Beta.

"As restrições de IP dinâmico para IIS 7.0 são um módulo que fornece proteção contra negação de serviço e ataques de força bruta em servidores e sites da Web. Essa proteção é fornecida bloqueando temporariamente os endereços IP dos clientes HTTP que fazem um número incomumente alto de solicitações simultâneas ou que fazem um grande número de solicitações em um pequeno período de tempo ". http://learn.iis.net/page.aspx/548/using-dynamic-ip-restrictions/

Exemplo:

Se você definir os critérios para bloquear depois X requests in Y millisecondsou X concurrent connections in Y millisecondso endereço IP for bloqueado Y milliseconds, as solicitações serão novamente permitidas.

notandy
fonte
1
Você sabe se isso causou algum problema em rastreadores como o Googlebot?
Helephant
1
É agora liberado e vem com IIS a partir da versão 8 - iis.net/learn/get-started/whats-new-in-iis-8/...
Matthew Steeples
Eu adoraria usar isso, mas NÃO permite que você acelere <location>. É todo pedido para o aplicativo ou nenhum.
Kasey Speakman
Não parece ser útil se os servidores da Web estiverem atrás de um balanceador de carga, pois todo o tráfego parecerá ter o mesmo endereço IP. A menos que eu estou faltando alguma coisa óbvia ...
Dscoduc
11

Usamos a técnica emprestada deste URL http://www.codeproject.com/KB/aspnet/10ASPNetPerformance.aspx , não para otimização, mas para o Negial Of Service (DOS) de um homem pobre. Isso também é baseado em cache e pode ser semelhante ao que você está fazendo. Você está se esforçando para impedir ataques do DOS? Os roteadores certamente podem ser usados ​​para reduzir o DOS; você acha que um roteador pode lidar com a otimização necessária?

Rob Kraft
fonte
1
É praticamente o que já estamos fazendo - mas está funcionando muito bem :)
Jarrod Dixon