2010-01-29 16 views
8

Digamos que, en términos teóricos, tengo una acción de página/controlador en mi sitio web que hace algunas cosas muy pesadas. Toma alrededor de 10 segundos completar su operación.ASP.NET (MVC) Outputcache y solicitudes concurrentes

Ahora, utilizo el mecanismo de caché de salida de .NET para almacenarlo en caché durante 15 minutos (por ejemplo, uso [OutputCache(Duration = 900)]) ¿Qué ocurre si, después de 15 minutos, la memoria caché expira y 100 usuarios vuelven a solicitarla en esos 10 segundos? se necesita para hacer el procesamiento pesado?

  1. El material pesado se realiza sólo la primera vez, y hay algún mecanismo de bloqueo para que los otros 99 usuarios obtendrán el resultado caché
  2. El material pesado se hace 100 veces (y el servidor se ve mermada como puede tomar hasta 100 * 10 segundos)

Pregunta fácil tal vez, pero no estoy 100% seguro. Espero que sea el número uno, sin embargo :-)

¡Gracias!

Respuesta

4

Bueno, depende de cómo haya configurado IIS. Si tiene menos de 100 subprocesos de trabajo (digamos, 50), entonces el "elemento pesado" se realiza 50 veces, paralizando su servidor, y luego las 50 solicitudes restantes se servirán desde el caché.

Pero no, no hay un "mecanismo de bloqueo" en un resultado de acción en caché; eso sería contraproducente, en su mayor parte.

Editar: Creo que esto es cierto, pero las pruebas de Nick dicen lo contrario, y no tengo tiempo para probar ahora. ¡Inténtalo tú mismo! El resto de la respuesta no depende de lo anterior, sin embargo, y creo que es más importante.

En general, sin embargo, ninguna solicitud web, almacenada en caché o de otro modo, demorará 10 segundos en volver. Si estuviera en su lugar, vería de alguna manera el pre-cálculo de la parte más difícil de la solicitud. Todavía puede almacenar en caché el resultado de la acción si desea almacenar en caché el HTML, pero parece que su problema es algo más grande que eso.

Usted podría también quiere consider asynchronous controllers. Finalmente, tenga en cuenta que aunque IIS y ASP.NET MVC no se bloquean en este cálculo pesado, podría hacerlo. Si usa controladores asíncronos combinados con un bloqueo en el cálculo, entonces obtendrá efectivamente el comportamiento que está solicitando. Realmente no puedo decir si esa es la mejor solución sin saber más sobre lo que estás haciendo.

+0

Gracias, cielos Realmente, no tengo una solicitud que demore 10 segundos, pero exagero mucho para ilustrar un punto. Solo tenía curiosidad por saber qué sucedería en tal escenario. ¡Gracias! Puede considerar la implementación de controladores asíncronos. – Razzie

+0

Ha pasado un tiempo ... simplemente hice una prueba y estoy seguro de que no se bloquea, como usted dijo. Gracias. – Razzie

3

Parece bloquear aquí, haciendo una prueba sencilla:

<%@ OutputCache Duration="10" VaryByParam="*" %> 

protected void Page_Load(object sender, EventArgs e) 
{ 
    System.Threading.Thread.Sleep(new Random().Next(1000, 30000)); 
} 

La primera página golpea el un punto de interrupción allí, a pesar de que ha dejado de dormir ... ninguna otra petición acierta en un punto de interrupción en el método Load. ..permite que termine el primero y devuelve ese resultado a todos los que han solicitado esa página.

Nota: esto fue más fácil de probar en un escenario de formularios web, pero dado que este es un aspecto compartido de los marcos, puede hacer la misma prueba en MVC con el mismo resultado.

Aquí es una forma alternativa de prueba:

<asp:Literal ID="litCount" runat="server" /> 

public static int Count = 0; 

protected void Page_Load(object sender, EventArgs e) 
{ 
    litCount.Text = Count++.ToString(); 
    System.Threading.Thread.Sleep(10000); 
} 

Todas las páginas en cola mientras que la primera solicitud se va a dormir tendrán la misma salida de recuento.

+0

¿Estás probando en WebDev? Se comporta * muy * diferentemente que IIS en un servidor multi-core. –

+0

@Craig: Pruebas con IIS 7.5, Windows 7 x64 en un quad-core –

+0

Parece extraño. Tal vez estás usando el modo de depuración? Esto realmente * no debería * bloquearse. Las otras solicitudes deben obtener un error de caché. –

1

Hice una pequeña prueba que podría ayudar. Creo que lo que descubrí es que las solicitudes no almacenadas no se bloquean, y cada solicitud que ingresa mientras el caché está caducado y antes de que la tarea se complete TAMBIÉN desencadena esa tarea.

Por ejemplo, el código de abajo toma unos 6-9 segundos en mi sistema usando Cassini. Si envía dos solicitudes, con una separación de aproximadamente 2 segundos (es decir, dos pestañas del navegador), ambas recibirán resultados únicos. La última solicitud para finalizar también es la respuesta que se almacena en caché para solicitudes posteriores.

// CachedController.cs 
using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Web; 
using System.Web.Mvc; 

namespace HttpCacheTest.Controllers 
{ 
    public class CachedController : Controller 
    { 
     // 
     // GET: /Cached/ 

     [OutputCache(Duration=20, VaryByParam="*")] 
     public ActionResult Index() 
     { 
      var start = DateTime.Now; 

      var i = Int32.MaxValue; 
      while (i > 0) 
      { 
       i--; 
      } 
      var end = DateTime.Now; 

      return Content(end.Subtract(start).ToString()); 
     } 

    } 
} 
3

Vieja pregunta, pero me encontré con este problema e investigué.

código Ejemplo:

public static int Count; 
[OutputCache(Duration = 20, VaryByParam = "*")] 
public ActionResult Test() 
{ 
    var i = Int32.MaxValue; 
    System.Threading.Thread.Sleep(4000); 
    return Content(Count++); 
} 

ejecuta en un navegador, y parece bloquear y esperar.

Ejecútelo en diferentes navegadores (probé en IE y Firefox) y las solicitudes no se ponen en espera.

Por lo tanto, el comportamiento "correcto" tiene más que ver con el navegador que está utilizando que con la función de IIS.

Editar: Para aclarar - Sin bloqueo. El servidor se ve afectado por todas las solicitudes que logran entrar antes de que se almacene el primer resultado, lo que posiblemente da como resultado un duro golpe en el servidor para solicitudes pesadas. (O si se llama a un sistema externo, ese sistema podría reducirse si el servidor sirve muchas peticiones ...)

0

Usted debe verificar esta información here: "tiene un solo cliente que realiza múltiples peticiones simultáneas al servidor . El comportamiento predeterminado es que estas solicitudes serán serializadas;

Por lo tanto, si la solicitud simultánea del cliente único se serializa, la solicitud posterior utilizará la memoria caché. Eso explica que algún comportamiento aparezca en alguna de las respuestas anteriores (@ mats-nilsson y @ nick-craver)

El contexto que nos mostró es de usuarios múltiples, que afectará al servidor al mismo tiempo y su servidor se ocupará hasta que haya completado al menos una solicitud y haya creado la memoria caché de resultados, y la use para la próxima solicitud. Entonces, si desea serializar a varios usuarios que solicitan el mismo recurso, debemos entender cómo funciona la solicitud serializada para un solo usuario. ¿Es eso lo que quieres?

Cuestiones relacionadas