2009-09-24 22 views
7

Tengo una aplicación que usa MSMQ para el procesamiento asincrónico de ciertas cosas.Forma correcta de alojar un * stable * Servicio de ventanas WCF MSMQ

Puedo utilizar WCF para colocar mensajes en la cola y tienen un oyente WCF MSMQ (un servicio de Windows) para recibir mensajes y tratar con ellos.

Mi problema es mantener esto estable. ¿Cuál es la forma correcta de tratar (por ejemplo) el servidor de cola (que es un cuadro separado) que baja? El otro día sucedió esto y el servicio solo se sentó allí; no se lanzaron excepciones, solo dejó de recibir mensajes. Me gustaría arrojar una excepción cuando el servidor de cola se cayó y luego volver a intentar conectarlo hasta que pueda.

También he notado que la ejecución de una "parada" en el servicio a menudo hace que se cuelgue durante bastante tiempo antes de que finalmente se detiene.

Cualquier código o sugiere crítica sería bienvenido. Obviamente, primero hice Google para esto, pero la mayoría de los ejemplos me muestran más o menos lo que ya tengo y me gustaría que mi sistema sea más sólido que eso.

Actualmente tengo esto:

(Nota: IMyExampleServiceContract es mi contrato de servicio WCF y QueueHandler es lo que lo implementa)

namespace xyz.MyExample.MSMQListener 
{ 
    /// <summary> 
    /// The class that handles starting and stopping of the WCF MSMQ Listener windows service. 
    /// It will respond to start and stop commands from within the windows services administration snap-in 
    /// It creates a WCF NetMsmqBinding that watches a particular queue for messaages defined by a contract 
    /// in the ServiceContracts project. 
    /// </summary> 
    public partial class MsmqListenerService : ServiceBase 
    { 
     /// <summary> 
     /// The WCF service host 
     /// </summary> 
     private ServiceHost _serviceHost; 

     /// <summary> 
     /// Defines the maximum size for a WCF message 
     /// </summary> 
     private const long MaxMessageSize = 1024 * 1024 * 1024; // 1 gb 
     /// <summary> 
     /// Defines the maximum size for a WCF array 
     /// </summary> 
     private const int MaxArraySize = 1024 * 1024 * 1024; // 1 gb 

     /// <summary> 
     /// The queue name 
     /// </summary> 
     private readonly string _queueName; 
     /// <summary> 
     /// The queue server 
     /// </summary> 
     private readonly string _queueServer; 

     /// <summary> 
     /// Initializes a new instance of the <see cref="MsmqListenerService"/> class. 
     /// </summary> 
     public MsmqListenerService() 
     { 
      InitializeComponent(); 
      using (ConfigManager config = new ConfigManager()) 
      { 
       _queueName = config.GetAppSetting("QueueName"); 
       _queueServer = config.GetAppSetting("QueueServer"); 
      } 
     } 

     /// <summary> 
     /// When implemented in a derived class, executes when a Start command is sent to the service by the Service Control Manager (SCM) or when the operating system starts (for a service that starts automatically). Specifies actions to take when the service starts. 
     /// <para> 
     /// The logic in this method creates a WCF service host (i.e. something that listens for messages) using the <see cref="IMyExampleServiceContract"/> contract. 
     /// The WCF end point is a NetMSMQBinding to the MyExample MSMQ server/queue. 
     /// It sets up this end point and provides a class to handle the messages received on it. 
     /// The NetMSMQBinding is a Microsoft WCF binding that handles serialisation of data to MSMQ. It is a ms proprietary format and means that the message on the queue 
     /// can only be read by a WCF service with the correct contract information. 
     /// </para> 
     /// </summary> 
     /// <param name="args">Data passed by the start command.</param> 
     protected override void OnStart(string[] args) 
     { 
      try 
      { 
       Logger.Write("MyExample MSMQ listener service started.", StandardCategories.Information); 

       Uri serviceUri = new Uri("net.msmq://" + QueueServer + QueueName); 

       NetMsmqBinding serviceBinding = new NetMsmqBinding(); 
       serviceBinding.Security.Transport.MsmqAuthenticationMode = MsmqAuthenticationMode.None; 
       serviceBinding.Security.Transport.MsmqProtectionLevel = System.Net.Security.ProtectionLevel.None; 
       serviceBinding.MaxReceivedMessageSize = MaxMessageSize; 
       serviceBinding.ReaderQuotas.MaxArrayLength = MaxArraySize; 

       //QueueHandler implements IMyExampleServiceContract 
       _serviceHost = new ServiceHost(typeof(QueueHandler)); 
       _serviceHost.AddServiceEndpoint(typeof(IMyExampleServiceContract), serviceBinding, serviceUri); 

       _serviceHost.Open(); 
       Logger.Write("MyExample MSMQ listener service completed OnStart method.", StandardCategories.Information); 
      } 
      catch (Exception ex) 
      { 
       ExceptionReporting.ReportException(ex, "DefaultExceptionPolicy"); 
       throw; 
      } 
     } 

     /// <summary> 
     /// Gets the name of the queue to send to. 
     /// This is retrieved from the application settings under QueueName 
     /// </summary> 
     private string QueueName 
     { 
      get { return _queueName; } 
     } 

     /// <summary> 
     /// Gets the name of the queue server to send to. 
     /// This is retrieved from the application settings under QueueServer 
     /// </summary> 
     private string QueueServer 
     { 
      get { return _queueServer; } 
     } 

     /// <summary> 
     /// When implemented in a derived class, executes when a Stop command is sent to the service by the Service Control Manager (SCM). Specifies actions to take when a service stops running. 
     /// </summary> 
     protected override void OnStop() 
     { 
      if (_serviceHost != null) 
      { 
       _serviceHost.Close(); 
       _serviceHost = null; 
      } 
     } 

     /// <summary> 
     /// The main entry point for the application. 
     /// </summary> 
     public static void Main() 
     { 
      //Code will have to be compiled in release mode to be installed as a windows service 
      #if (!DEBUG) 
       try 
       { 
        Logger.Write("Attempting to start queue listener service.", StandardCategories.Information); 
        ServiceBase[] ServicesToRun; 
        ServicesToRun = new ServiceBase[] 
          { 
          new MsmqListenerService() 
          }; 
        ServiceBase.Run(ServicesToRun); 
        Logger.Write("Finished ServiceBase.Run of queue listener service.", StandardCategories.Information); 
       } 
       catch (Exception e) 
       { 
        ExceptionReporting.ReportException(e, "DefaultExceptionPolicy"); 
        throw; 
       } 
      #else 
       //This allows us to run from within visual studio 
       MsmqListenerService service = new MsmqListenerService(); 
       service.OnStart(null); 
       System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite); 
      #endif 

     } 
    } 
} 

Respuesta

5

No estoy seguro de por qué su host de servicio está colgando, pero sin duda puede pensar en un par de cosas para tratar de hacer que sea más fiable:

  • me aseguraría a enganchar en el Faulted event del host del servicio. Por lo general, es un buen lugar para reconocer que necesita reaparecer su host nuevamente.
  • me gustaría establecer un camino para el servicio de ping a sí mismo, por tener una cola especial el estado de salud en el servidor de cola remota y tienen un segundo servicio WCF a medida que escucha en esa cola. Entonces tendría el host de servicio solo use los mensajes en esa cola regular y controlar que:

a) Puede enviarlos con éxito y

b) que los mensajes están siendo recogidos y procesados ​​por el servicio local de salud WCF escuchando en esa cola. Eso podría usarse para detectar algunas posibles condiciones de error.

+0

El evento de fallo se ve muy bien. Supongo que crees que no entrará en este estado si falla el servidor MSMQ. (Como se está recomendando el servicio "ping") El servicio "ping" suena como una buena solución temporal para comprobar la disponibilidad de servidores, sólo pensé que no habría habido algo integrado en WCF que lidiar con esto por mí. De todos modos, voy a dar el caso Faulted una oportunidad, así que gracias por dirigirme en su dirección. – David

+0

Para ser honesto, no sé a ciencia cierta, por lo tanto porqué recomendé ambas opciones :) – tomasr

3

El oyente subyacente WCF MSMQ es, probablemente, una excepción antes de que llegue su código. Lo cual es una situación frustrante porque parece que no pasa nada y lo peor es que su mensaje se descarta. Active WCF service tracing en su archivo de configuración del servicio.

Ahora bien, cuando se ejecuta el servicio que va a rastrear y le dará más detalles. En lugar de forzar la vista a través del XML, abra este archivo de registro con el MS Service Trace Viewer.

Cuando tuve este problema que estaba recibiendo "System.ServiceModel.ProtocolException":

un mensaje de MSMQ entrante contenido no válido o inesperado .NET mensaje Framing información en su cuerpo. El mensaje no puede ser recibido. Asegúrese de que el remitente esté utilizando un contrato de servicio compatible con un SessionMode * correspondiente. Mi contrato de servicio había sido cambiado para tener el atributo SessionMode = SessionMode.Required, pero los clientes no estaban enviando mensajes con una transacción.

+0

Gracias por la idea, a su vez, en el trazado y darle una oportunidad. – David

1

Aunque WCF agrega algunas características geniales a MSMQ, a veces logrará su objetivo con la misma facilidad y con mayor control si codifica manualmente el procesamiento de MSMQ.

Si procesa manualmente su cola, podrá ver exactamente lo que está sucediendo & frente a las MessageQueueExceptions generadas, p. Ej. podrá capturar MessageQueueErrorCodes, como QueueNotFound o MachineNotFound.

Desafortunadamente esto también significa la gestión de la cola de mensajes dañados, la adición de las transacciones con el procesamiento, la adición de un período de tiempo de espera en la cola, etc. Todo el material que WCF bien toma el cuidado de usted.

La principal ventaja de utilizar WCF es que se puede entonces Se utilizó para crear instancias de una aplicación web, en lugar de tener un servicio de Windows que se ejecuta continuamente. Si no está utilizando este beneficio, realmente no veo ningún beneficio para WCF, simplemente está abstrayendo mucho de lo que necesita tocar y ver.


Sólo como una nota al margen; ¿quizás podría verificar si el servidor está disponible cuando coloca mensajes en la cola? Si el servidor está disponible para poner los mensajes en la cola, el oyente podrá procesarlos inmediatamente.

Cuestiones relacionadas