2012-02-23 20 views
5

que tienen un siguiente situación:NServiceBus: ¿cómo obtener una cola separada para cada tipo de mensaje que el receptor suscribe?

enter image description here

Por lo tanto, el receptor se suscribe a dos tipos de eventos: Eventa y eventB. NServiceBus crea la cola para el receptor (Receptor) y coloca mensajes de tipo eventA y eventB en la misma cola. La pregunta es si puedo configurar NServiceBus para usar colas separadas (ReceiverEventA y ReceiverEventB) para cada tipo de evento para el receptor. O puedo tener dos receptores en un solo proceso (y cada receptor en cola separada). La cosa es que EventA tarda mucho más en procesarse que EventB, y son independientes, por lo que si estuvieran en colas separadas, podrían procesarse simultáneamente.

Actualización: Si voy con enfoque ingenuo de esta manera, el receptor no puede iniciarse con excepción de referencia nula:

private static IBus GetBus<THandler, TEvent>() 
    {     
     var bus = Configure.With(new List<Type> 
            { 
             typeof(THandler), 
             typeof(TEvent), 
             typeof(CompletionMessage) 
            }) 
      .Log4Net() 
      .DefaultBuilder() 
      .XmlSerializer() 
      .MsmqTransport() 
      .IsTransactional(true) 
      .PurgeOnStartup(false) 
      .UnicastBus() 
      .LoadMessageHandlers() 
      .ImpersonateSender(false); 

     bus.Configurer.ConfigureProperty<MsmqTransport>(x => x.InputQueue, "Queue" + typeof(THandler).Name); 

     return bus.CreateBus().Start(); 
    } 

    [STAThread] 
    static void Main() 
    { 
     Busses = new List<IBus> 
        { 
         GetBus<ItemEventHandlerA, ItemEventA>(), 
         GetBus<ItemEventHandlerB, ItemEventB>() 
        };   

     Application.EnableVisualStyles(); 
     Application.SetCompatibleTextRenderingDefault(false); 
     Application.Run(new TestForm()); 
    } 

Excepción seguimiento de la pila es:

en NServiceBusTest2.WinFormsReceiver.Program .GetBusTHandler, TEvent en C: \ Users \ User \ Documents \ Visual Studio 2010 \ Projects \ NServiceBusTest2 \ NServiceBusTest2.WinFormsReceiver \ Program.cs: línea 57
en NServiceBusTest2.WinFormsReceiver.Program.Main() en C: \ Users \ Usuario \ Documentos \ Visual Studio 2 010 \ Projects \ NServiceBusTest2 \ NServiceBusTest2.WinFormsReceiver \ Program.cs: línea 26
en System.AppDomain._nExecuteAssembly (montaje RuntimeAssembly, args String [])
en System.AppDomain.ExecuteAssembly (String assemblyFile, assemblySecurity Evidencia, String [ ] args) en Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
en System.Threading.ThreadHelper.ThreadStart_Context (estado Object)
en System.Threading.ExecutionContext.Run (ExecutionContext executionContext, ContextCallback devolución de llamada, objeto de estado, Boolean ignoreSyncCtx)
en System.Threading.ExecutionContext.Run (ExecutionContext executionContext, ContextCallback callback, estado del objeto)
en System.Threading.ThreadHelper.ThreadStart()

Respuesta

5

Empecé a escribir una explicación más extensa de por qué NO debería tener dos procesos separados, en apoyo de mi comentario a la respuesta @stephenl publicado. NServiceBus básicamente impone una cola de entrada por proceso.

Por lo general, tendría dos procesos separados. EventAService leerá EventA de QForEventA, en un proceso separado de EventBService que leerá EventB de QForEventB.

Luego miré más detenidamente su código de ejemplo y me di cuenta de que estaba en una aplicación de Windows Forms. Duh! Ahora me siento un poco tonto. Por supuesto, solo puedes tener un proceso. ¡Imagínese si después de ejecutar Outlook también tuvo que iniciar MailService.exe para recibir realmente el correo!

Así que el problema es que tienes tiempos de procesamiento drásticamente diferentes para EventA y EventB en tu aplicación de Windows Forms. No tengo forma de saber qué es ese trabajo, pero esto es un poco extraño para una aplicación cliente.

La mayoría de las veces es un servicio que tiene un gran procesamiento, y cualquier mensaje recibido por un cliente es bastante liviano, algo así como "Entity X ha cambiado así que la próxima vez tendrá que cargarlo directamente de la base de datos "y el procesamiento implica simplemente eliminar algo de la memoria caché, ciertamente no es un proceso de larga ejecución.

Pero parece que por alguna razón el procesamiento de su cliente lleva más tiempo, lo que en una aplicación Windows Forms es preocupante debido a la preocupación de clasificación hilo de interfaz de usuario, bloqueando el hilo de interfaz de usuario, etc.

que sugeriría un sistema de donde no hace todo el procesamiento en el controlador NServiceBus en su aplicación WinForms, sino que lo ordena en otro lugar. Tíralo al ThreadPool como un elemento de trabajo, o algo así. O coloque los artículos de larga ejecución en una cola y tenga un crujido de hilo de fondo en aquellos a su propia velocidad. De esa forma todo lo que hace el controlador de mensajes NServiceBus es "Sí, recibí el mensaje. Muchas gracias". Entonces realmente no debería importar si los mensajes de NServiceBus se procesan uno a la vez.

Actualización:

En los comentarios, el OP se pregunta qué sucede si tiras la obra al ThreadPool después NServiceBus los acabados de su recepción. Esto es, por supuesto, la otra cara de este enfoque: después de que NServiceBus haya terminado, estás solo, si falla en el ThreadPool, te corresponde a ti crear tu propia lógica de reintento, o simplemente atrapar la excepción, alertar al usuario de la aplicación WinForms y dejarla morir.

Obviamente eso es óptimo, pero plantea la pregunta: ¿exactamente qué tipo de trabajo está tratando de lograr en la aplicación WinForms? Si la robustez que ofrece NServiceBus (los reintentos automáticos y la cola de errores para mensajes tóxicos) es una pieza fundamental de este rompecabezas, ¿por qué está sucediendo en una aplicación WinForms en primer lugar? Esta lógica probablemente deba descargarse a un servicio externo a la aplicación WinForms, donde tener colas separadas para cada tipo de mensaje (mediante la implementación de servicios separados) se vuelve fácil, y luego solo las piezas que afectan a la interfaz de usuario son enviadas al cliente WinForms. Cuando los mensajes a la interfaz de usuario solo afectan a la interfaz de usuario, procesarlos es casi siempre trivial, y no tendrá que descargar el ThreadPool para mantenerse al día.

Hablando directamente a la situación que describió en el GitHub Issue, esto realmente suena como el tipo de escenario donde los procesos separados para cada tipo de mensaje son exactamente la solución prescrita. Escuché que parece abrumador implementar y administrar tantos procesos, pero creo que descubrirá que no es tan malo como parece. Incluso hay ventajas: si tiene que volver a implementar su conector con Amazon.com, por ejemplo, solo tiene que volver a desplegar ESE punto final, sin tiempo de inactividad para ninguno de los otros, o cualquier preocupación de que los errores se hayan presentado a los demás.

Para facilitar la implementación, con suerte está utilizando un servidor de integración continua, y luego registrarse en herramientas como DropkicK que ayudan a implementar las secuencias de comandos. Personalmente, mi herramienta favorita de implementación es Robocopy. Incluso algo tan simple como 1) NET STOP ServiceName, 2) ROBOCOPY, 3) NET START ServiceName es bastante eficaz.

+0

Estaba pensando en tirar a ThreadPool como un elemento de trabajo, pero ¿y si falla allí? Handler terminará su trabajo, por lo que se eliminará el mensaje de la cola y no hay una forma estándar de recuperarlo (¿o me falta algo?). Estamos utilizando formularios de Windows como una herramienta de gestión de procesos. He mencionado un ejemplo más concreto aquí: https://github.com/NServiceBus/NServiceBus/issues/219 – Giedrius

+1

@Giedrius: proporcioné actualizaciones en el cuerpo de la respuesta. –

+0

Gracias por su ayuda. Para responder a su pregunta: WinForms es para tener un tablero de instrumentos para pausar/reanudar, también para ver el estado actual de los canales de venta, que es qué procesamiento. Encontramos la forma de tener varios receptores usando AppDomains por separado, pero en general es una solución dolorosa. Parece que tendremos que aceptar soluciones con AppDomains, buscar alternativas para NServiceBus, reconsiderar la implementación (ahora tenemos cc.net + clickOnce) y tener el tablero y los manejadores configurados como piezas separadas. – Giedrius

2

Usted no necesita, sólo tiene que crear un controlador para cada evento en el receptor. Por ejemplo

public class EventAHandler : IHandleMessages<EventA>{} 

public class EventBHandler : IHandleMessages<EventB>{} 

Si desea separar las colas, debe colocar cada manejador en un punto final separado. ¿Tiene sentido?

También creo que su diagrama necesita un poco de trabajo. Es algo de etiquetado, estoy seguro, pero la forma en que lo veo es que Host es el editor real, no los puntos finales del cliente (que usted ha denominado Publisher A y Publisher B).

ACTUALIZACIÓN: Es un ejemplo sencillo pero ilustra lo que quiero decir - https://github.com/sliedig/sof9411638

que hemos ampliado la muestra de dúplex completo que viene con NServiceBus para incluir tres criterios de valoración adicionales, dos de los cuales se suscriben a eventos que están siendo publicados por el servidor respectivamente y un punto final que maneja ambos. HTH.

+0

con respecto a la denominación, es solo muestra, por lo que puede estar en lo cierto, esos son más como remitentes y el host es editor. En cuanto a las colas, si ambos eventos se colocan en la misma cola, ¿no es que si EventA es el primero y lleva mucho tiempo procesarlo, EventB esperará hasta que se procese EventA? Creo que si estuvieran en colas separadas, podrían procesarse de manera conciente o ¿estoy equivocado? – Giedrius

+1

Si lo configuró para usar múltiples hilos, entonces probablemente no tenga un gran problema si se bloquean entre sí. Dicho esto, es posible que aún tenga una gran afluencia de EventAs que atarán todos los hilos y bloquearán el procesamiento de EventBs. Si eso es algo que te preocupa mucho, debes tener un punto final de recepción separado para EventBs. –

+0

Bueno, configurarlo para usar múltiples hilos requiere una licencia comercial, como yo lo entiendo :) Y no tenemos tantos mensajes, para comprar una licencia comercial para eso, solo los tiempos promedio de procesamiento de EventA y EventB son muy diferentes. ¿Podría señalarme una muestra del receptor que tiene dos puntos finales receptores? – Giedrius

Cuestiones relacionadas