2009-12-14 14 views
5

Actualmente estoy teniendo problemas para perder un mensaje. Este error ocurre raramente, pero sucede con suficiente frecuencia como para ser molesto. Aquí está el contexto del problema:¿Es posible perder mensajes usando MSMQ MessageQueue.Peek con un tiempo de espera excedido?

  • He activado el diario de mensajes en goldmine_service_queue, un MSMQ en el servidor de Windows 2003.
  • Puedo probar que el mensaje se está insertando en goldmine_service_queue ya que el mensaje aparece en el diario de mensajes. Esta información proporciona información de tiempo sobre cuándo desapareció el mensaje.
  • Las funciones de registro utilizan http://logging.apache.org/log4net/index.html
  • Los registros no muestran errores.
  • La función de trabajador (que se muestra a continuación) se ejecuta dentro de un hilo de un servicio de Windows. Es responsable de echar un vistazo a los mensajes (elementos de trabajo) de la cola y procesarlos.
  • De los registros, sospecho fuertemente que mi problema podría estar relacionado con MessageQueue.Peek y el comportamiento de tiempo de espera.

¿Es posible que se produzca el tiempo de espera y la recepción del mensaje al mismo tiempo? ¿Hay una manera mejor para mí de manejar la comprobación de detención del servicio para ayudar a evitar este error?

 private void workerFunction() 
    { 

     logger.Info("Connecting to queue: " + Settings.Default.goldmine_service_queue); 
     MessageQueue q = new MessageQueue(Settings.Default.goldmine_service_queue); 
     q.Formatter = new ActiveXMessageFormatter(); 

     while (serviceStarted) 
     { 

      Message currentMessage = null; 

      try 
      { 
       currentMessage = q.Peek(new TimeSpan(0,0,30)); 
      } 
      catch (System.Messaging.MessageQueueException mqEx) 
      { 
       if (mqEx.ToString().Contains("Timeout for the requested operation has expired")) 
       { 
        logger.Info("Check for service stop request"); 
       } 
       else 
       { 
        logger.Error("Exception while peeking into MSMQ: " + mqEx.ToString()); 
       } 
      } 
      catch (Exception e) 
      { 
       logger.Error("Exception while peeking into MSMQ: " + e.ToString()); 
      } 

      if (currentMessage != null) 
      { 

       logger.Info(currentMessage.Body.ToString()); 
       try 
       { 
        ProcessMessage(currentMessage); 
       } 
       catch (Exception processMessageException) 
       { 
        logger.Error("Error in process message: " + processMessageException.ToString()); 
       } 

       //Remove message from queue. 
       logger.Info("Message removed from queue."); 
       q.Receive(); 
       //logPerformance(ref transCount, ref startTime); 
      } 


     }//end while 

     Thread.CurrentThread.Abort(); 

    } 

Respuesta

5

No creo que los mensajes se debe perder en base a una revisión rápida, pero están trabajando de una manera muy rara, con un montón de posibilidades para condiciones de carrera.

¿Por qué no acaba de recibir el mensaje y pasarlo a ProcessMessage (si ProcessMessage falla, está realizando una lectura de todos modos). Si necesita manejar múltiples receptores, realice la recepción en una transacción MSMQ para que el mensaje no esté disponible para otros receptores pero no se elimine de la cola hasta que se haya comprometido la transacción.

Además, en lugar de sondear la cola, ¿por qué no hacer una recepción asincrónica y dejar que el grupo de subprocesos maneje la finalización (donde debe llamar al EndReceive). Esto le ahorra atar un hilo, y no necesita cerrar el servicio de casos especiales (cierre la cola de mensajes y luego llame al MessageQueue.ClearConnectionCache();).

Además, abortar el hilo es una forma realmente mala de salir, simplemente regrese de la función de inicio del hilo.

+0

Gracias por la respuesta: - en lo que respecta a las condiciones de carrera: No estoy seguro de si veo las condiciones de carrera que está mencionando. De acuerdo con http://msdn.microsoft.com/en-us/library/t5te2tk0.aspx, MessageQueue.Peek (Timeout) es una llamada de bloqueo. - Me preocupa utilizar un patrón asíncrono ya que ProcessMessage realiza algunas operaciones COM. No sé si estas operaciones son seguras para hilos. Estoy de acuerdo con intentar utilizar Receive() en lugar de peak excepto que no sé cómo crear la oportunidad de detener el ciclo while debido a una parada del servicio. –

+0

Re: COM en 'ProcessMessage': no debería ser un problema si solo un subproceso está ejecutando' ProcessMessage' (que se puede lograr en el caso async iniciando una nueva recepción asincrónica después de que 'ProcessMessage' se haya completado. En la asincrónica caso de que cierre la cola (y vacíe la memoria caché --- en función de algunas búsquedas esto parece ser necesario para cerrar realmente la conexión) y cualquier recepción pendiente será cancelada. No hay ningún ciclo para cancelar. – Richard

1

Voy a ser que cambiando currentMessage = q.Peek(new TimeSpan(0,0,30)); a currentMessage = q.Receive(); se solucionará su problema. He estado usando MSMQ para el envío de mensajes en el mismo contexto pero solo utilizo peek (y espero una excepción de tiempo de espera) para determinar si la cola está vacía. La llamada al Receive está bloqueando, así que planifique en consecuencia.

Editar: también para su captura de excepciones: puede verificar si su excepción es una excepción de tiempo de espera al comparar el código de error.

mqe.MessageQueueErrorCode == MessageQueueErrorCode.IOTimeout 

donde mqe es la excepción de su cola de mensajes. Es posible que te guste más que una comparación de cadenas.

+0

Yo uso 'Mensaje rsp = colaStatus.ReceiveByCorrelationId (correlationId, new TimeSpan (0, timeOut, 0)); '. Estoy confundido si 5 minutos es de muy alto nivel para el tiempo de espera. En mi humilde opinión, creo que es mejor que sea inferior a 1 minuto. – Kiquenet

3

Solo algunos comentarios para aclarar cómo funciona MSMQ aquí.

"Puedo probar que el mensaje se está insertando en goldmine_service_queue ya que el mensaje aparece en el diario de mensajes.

El mensaje entra en la cola del diario cuando el mensaje original se elimina de goldmine_service_queue. Por lo tanto, puede decir que el mensaje se entregó correctamente a la cola Y se eliminó de la cola con éxito.

"Sospecho fuertemente que mi problema podría estar relacionado con MessageQueue.Peek y el comportamiento de tiempo de espera".

A Peek no hace nada para eliminar el mensaje de la cola. Solo "q.Receive();" hace eso. En su código, no hay una conexión explícita entre el mensaje que se ve y el que se está recibiendo. "q.Receive();" solo dice "recibir mensaje de la parte superior de la cola". En un entorno de subprocesos múltiples, podría esperar que los mensajes se leyeran de forma inconsistente; algunos podrían leerse y procesarse varias veces. Debería obtener el ID del mensaje Ojeada y usar ReceiveByID para que solo pueda recibir el mensaje peeked.

Cuestiones relacionadas