2009-12-14 8 views
5

Attualmente sto avendo un problema di perdere un messaggio. Questo errore si verifica raramente, ma accade abbastanza spesso da risultare fastidioso. Ecco il contesto del problema:È possibile perdere i messaggi utilizzando MSMQ MessageQueue.Peek con un timeout?

  • Ho attivato il giornale dei messaggi su goldmine_service_queue, un MSMQ su un server Windows 2003.
  • Posso provare che il messaggio viene inserito in goldmine_service_queue poiché il messaggio viene visualizzato nel giornale dei messaggi. Queste informazioni forniscono informazioni sul momento in cui il messaggio è scomparso.
  • Le funzioni di registrazione utilizzano http://logging.apache.org/log4net/index.html
  • I registri non mostrano errori.
  • La funzione di lavoro (illustrata di seguito) viene eseguita all'interno di un thread di un servizio di Windows. È responsabile di dare una occhiata ai messaggi (oggetti di lavoro) dalla coda ed elaborarli.
  • Dai registri, ho il forte sospetto che il mio problema potrebbe riguardare MessageQueue.Peek e il comportamento scaduto.

È possibile che il timeout e la ricezione del messaggio si verifichino contemporaneamente? C'è un modo migliore per me di gestire il controllo degli arresti del servizio per evitare questo errore?

 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(); 

    } 

risposta

5

Non credo che nessun messaggio dovrebbe perdere sulla base di una breve recensione, ma si sta lavorando in un modo molto strano con un sacco di spazio per le condizioni di gara.

Perché non solo ricevere il messaggio e passarlo a ProcessMessage (se ProcessMessage non riesce, si sta comunque effettuando una lettura). Se è necessario gestire più ricevitori, effettuare la ricezione in una transazione MSMQ in modo che il messaggio non sia disponibile per altri destinatari ma non rimossi dalla coda fino a quando la transazione non viene eseguita.

Inoltre, anziché eseguire il polling della coda, perché non eseguire una ricezione asincrona e lasciare che il pool di thread gestisca il completamento (in cui è necessario chiamare EndReceive). Ciò consente di risparmiare un thread e non è necessario arrestare il servizio caso speciale (chiudere la coda dei messaggi e quindi chiamare MessageQueue.ClearConnectionCache();).

Inoltre, interrompere il thread è un modo davvero pessimo per uscire, basta tornare dalla funzione di avvio del thread.

+0

Grazie per la risposta: - per quanto riguarda le condizioni di gara: non sono sicuro di vedere le condizioni di gara che menzionate. Secondo http://msdn.microsoft.com/en-us/library/t5te2tk0.aspx, MessageQueue.Peek (Timeout) è una chiamata bloccante. - Sono preoccupato per l'utilizzo di un modello asincrono poiché ProcessMessage esegue alcune operazioni COM. Non so se queste operazioni sono thread-safe. io sono ok con il tentativo di utilizzare ricezione() al posto del picco solo che non so come creare la possibilità di fermare il ciclo while a causa di un arresto di servizio. –

+0

Re: COM in 'ProcessMessage': non dovrebbe essere un problema se solo un thread sta eseguendo' ProcessMessage' (che può essere ottenuto nel caso async solo avviando una nuova ricezione asincrona dopo il completamento di 'ProcessMessage'. In async caso si chiude la coda (e si svuota la cache --- in base ad alcune ricerche questo sembra essere necessario per chiudere realmente la connessione) e qualsiasi ricezione in attesa verrà annullata .Non c'è loop da cancellare. – Richard

1

Sono pronto ad essere che il cambio currentMessage = q.Peek(new TimeSpan(0,0,30)); a currentMessage = q.Receive(); risolverà il problema. Sto usando MSMQ per il passaggio dei messaggi nello stesso identico contesto, ma uso solo peek (e aspetto un'eccezione di timeout) per determinare se la coda è vuota. La chiamata a Receive sta bloccando, quindi pianifica di conseguenza.

Modifica: anche per il rilevamento delle eccezioni - è possibile verificare se l'eccezione è un'eccezione di timeout confrontando il codice di errore.

mqe.MessageQueueErrorCode == MessageQueueErrorCode.IOTimeout 

in cui mqe è il tuo messaggio in coda all'eccezione. Potrebbe piacerti meglio di un confronto tra stringhe.

+0

Io uso 'Message rsp = colaStatus.ReceiveByCorrelationId (correlationId, new TimeSpan (0, timeOut, 0)); '. Sono confuso se 5 minuti sono di altissimo livello per il timeout. IMHO, penso che sia meglio di un minuto. – Kiquenet

3

Solo alcuni commenti per chiarire come funziona MSMQ qui.

"Posso provare che il messaggio viene inserito in goldmine_service_queue poiché il messaggio viene visualizzato nel giornale dei messaggi.

Il messaggio va nella coda di registrazione quando il messaggio originale viene rimosso da goldmine_service_queue. Quindi puoi dire che il messaggio è stato consegnato correttamente alla coda E rimosso con successo dalla coda.

"Ho il forte sospetto che il mio problema potrebbe riguardare MessageQueue.Peek e il comportamento scaduto."

Un Peek non fa nulla per rimuovere il messaggio dalla coda. Solo "q.Ricevi();" lo fa Nel tuo codice non esiste una connessione esplicita tra il messaggio che viene visualizzato e quello ricevuto. "Q.Receive();" dice semplicemente "ricevi messaggi dalla parte superiore della coda". In un ambiente multi-thread, ci si potrebbe aspettare che i messaggi vengano letti in modo incoerente: alcuni potrebbero essere visualizzati e elaborati più volte. Dovresti ottenere l'ID del messaggio Peeked e utilizzare ReceiveByID in modo da poter ricevere solo il messaggio Peek.

Problemi correlati