2012-09-30 28 views
9

Estoy creando una biblioteca de registro que almacena todo en una tabla de Azure. Escrito a esa mesa, obviamente, necesita una gran cantidad de tiempo (no más de 1 segundo, pero aún así es demasiado para hacer que la espera por el usuario), por lo que el método de registro devuelve una instancia de LogResult, aquí está la claseImpedir que IIS elimine una tarea antes de que finalice

public class LogResult 
{ 
    public string Id { get; set; } 
    public Task LoggingTask { get; set; } 

    public LogResult(string id, Task task) 
    { 
     Id = id; 
     LoggingTask = task; 
    } 
} 

Y aquí es cómo el método de registro termina

return new LogResult(id, Task.Factory.StartNew(() => 
    DoLogInAzure(account, id, exception, request)) 
); 

para dar a la persona que llama la opción de esperar a que se complete (si se trata de una aplicación de consola, por ejemplo). El problema al que me enfrento es que IIS no debería esperar esperar antes de devolver al usuario la respuesta ... y si no lo espero, IIS no siempre ejecutar la tarea. La idea es mostrarle al usuario un mensaje "... Si nos contacta, asegúrese de mencionar su número de problema, XXX" y no lo haga esperar hasta que se haya escrito la entrada del registro.

¿Hay alguna manera de forzar a IIS a esperar hasta que la tarea finalice, incluso después de que devolvió la respuesta? Estoy pensando que podría necesitar codificar un servicio de Windows que acepte la solicitud de forma asíncrona, pero parece mucho trabajo simplemente agregar una entrada de registro ... especialmente si puedo obligar a IIS a esperarla.

¡Gracias por cualquier idea!

+0

He oído que IIS reinicia 'sitios web' que requieren demasiada memoria ... –

+0

Entiendo :) sin embargo, no creo que ese sea mi caso, como si esperara a que la tarea termine, IIS termina el ejecución y la entrada de registro se escribe el 100% de las veces – g3rv4

+0

Como alternativa, podría simplemente utilizar una cola azul para iniciar sesión. Entonces, un manejador de mensajes separado puede escribir asincrónicamente en la tabla azul. Mejor aún, si obtiene un procesamiento de error, el mensaje permanece en la cola y se procesará nuevamente más tarde. –

Respuesta

8

Gracias a Damian Schenkelman y esa publicación de blog de Phil Haack, descubrí el problema y la solución. El problema es que IIS reutiliza los subprocesos cuando necesita manejar nuevas solicitudes. Y como no sabe que mi tarea está haciendo algo de trabajo, reutiliza ese hilo (lo cual tiene sentido). Entonces, solo tengo que notificar a IIS que estoy usando ese hilo y que no puede ser reutilizado (por lo tanto, tiene que reutilizar otro hilo, crear uno nuevo o esperar). Terminé usando mi propia TaskFactory que maneja la creación de tareas y registra automáticamente un notificador en IIS. Para completar, para ayudar a otra gente con el mismo problema que yo, y para leer otro sugerencias, esto es lo que he hecho

public class IISNotifier : IRegisteredObject 
{ 
    public void Stop(bool immediate) 
    { 
     // do nothing, I only run tasks if I know that they won't 
     // take more than a few seconds. 
    } 

    public void Started() 
    { 
     HostingEnvironment.RegisterObject(this); 
    } 

    public void Finished() 
    { 
     HostingEnvironment.UnregisterObject(this); 
    } 
} 

Y luego

public class IISTaskFactory 
{ 
    public static Task StartNew(Action act) 
    { 
     IISNotifier notif = new IISNotifier(); 
     notif.Started(); 
     return Task.Factory.StartNew(() => { 
      act.Invoke(); 
      notif.Finished(); 
     }); 
    } 
} 

Ahora, cuando quiero empezar un registro de la tarea que acaba de hacer

return new LogResult(id, IISTaskFactory.StartNew(() => 
    DoLogInAzure(account, id, exception, request)) 
); 

Se puede ver (y descargar el código) en https://github.com/gmc-dev/IISTask

11

This post de Phil Hack se trata de ejecutar tareas en segundo plano en una aplicación ASP.NET.

+0

Acabo de terminar de leer ese artículo ... Gracias, es realmente algo que voy a necesitar. Sin embargo, no explica por qué mi tarea no siempre se ejecuta ... Lo estoy ejecutando en mi máquina, y generar 100 excepciones en un minuto almacena alrededor de 92 si no espero a que la tarea se complete. El grupo de aplicaciones no se reinicia tampoco ... ¿Eso tiene sentido? – g3rv4

+0

¿Cómo está generando las excepciones? Cuando espere, solo congelará el hilo en ejecución hasta que todas las Tareas se completen, pero eso no debería ser necesario si el proceso aún se está ejecutando. ¿Se detiene el proceso antes de ejecutar las tareas? Utilice la lógica simulada (por ejemplo, impresión para depurar ventana) en cada tarea para asegurarse de que todas las tareas tuvieron tiempo para comenzar y completar. –

+0

La excepción que estoy tratando ahora, es b/c. Estoy asignando null en una cadena no anulable en una Entidad ... luego, el 'context.SaveChanges();' falla. Sin embargo, si genero una excepción y no hago nada más, siempre está almacenada. La forma de hacerlo fallar genera muchas excepciones en un corto período de tiempo. Supongo que IIS puede reutilizar el hilo para manejar nuevas solicitudes, y es entonces cuando se cancela mi tarea. Estoy haciendo 100 solicitudes (que arrojan una excepción) en menos de 10 segundos, y es cuando falla. – g3rv4

0

La información no es suficiente, pero sospecho que puede estar relacionada con GC y las referencias si funciona cuando espera la tarea. Para su propósito, una mejor manera es usar ETW (EventProvider) y establecer el ActivityId para cada solicitud. Simplemente configure una sesión de ETW que pueda redirigir todos los mensajes a un archivo. Puede mostrar ActivityId (una Guid) al usuario final.

+2

'Task' se ejecutará bien, incluso si no hay ninguna referencia a él, ese no es el problema. – svick

+0

Para verificar si ese es el problema, almacené todas las referencias a la 'Tarea' en una instancia única ... sin embargo, eso no cambió su comportamiento ... Hasta ahora, lo único que funciona, está esperando para la tarea antes de que se obtenga la respuesta – g3rv4

0

Perdón por no agregar esto como comentario, no tengo suficiente representante.

https://msdn.microsoft.com/en-us/library/system.web.hosting.iregisteredobject(v=vs.110).aspx

Las aplicaciones pueden tener una sola instancia de un tipo registrado.

Esto parece indicar que la respuesta aceptada de Gervasio Marchand es algo incorrecto, ya que cada llamada a su método de ayuda estática crea un nuevo IISNotifier, que es un IRegisteredObject.

+1

, ¿pudo encontrar un ejemplo donde esta solución no funciona? Me encantaría ver algo así, ya que he utilizado este enfoque en toneladas de proyectos y nunca vi nada raro – g3rv4

Cuestiones relacionadas