studio mensajes logs log hacer debug como java multithreading performance messaging disruptor-pattern

java - logs - mensajes log android



Cómo usar un disruptor con múltiples tipos de mensajes (2)

Configure el disruptor para usar objetos que contengan una matriz de bytes de tamaño fijo (según lo recomendado por ¿Cómo se debe usar Disruptor (Patrón de disruptor) para construir sistemas de mensajes del mundo real?). En este caso, el hilo principal debe codificar los mensajes en matrices de bytes antes de publicarlos en el disruptor y cada una de las hebras de lógica de negocios debe decodificar los arrays de bytes en objetos al recibirlos. La desventaja de esta configuración es que los subprocesos de la lógica de negocios no están realmente compartiendo la memoria del disruptor, sino que están creando nuevos objetos (y, por lo tanto, creando basura) de la matriz de bytes proporcionada por el disruptor. La ventaja de esta configuración es que todos los subprocesos de lógica empresarial pueden leer varios tipos diferentes de mensajes del mismo disruptor.

Este sería mi enfoque preferido, pero a pesar de nuestros diferentes casos de uso, casi todos los lugares donde usamos el Disruptor está recibiendo o enviando a algún tipo de dispositivo de E / S, por lo que nuestra moneda básica son las matrices de bytes. Puede sortear la creación de objetos utilizando un enfoque de peso mosca para calcular. Para ver un ejemplo de esto, usé las clases Struct y Union de Javolution en un ejemplo que presenté en Devoxx ( https://github.com/mikeb01/ticketing ). Si puede manejar completamente el objeto antes de regresar de la llamada onEvent desde el controlador de eventos, este enfoque funciona bien. Si el evento necesita vivir más allá de ese punto, entonces necesita hacer algún tipo de copia de los datos, por ejemplo, deserializarlo en un objeto.

Configure el disruptor para usar un solo tipo de objeto, pero cree múltiples disruptores, uno para cada tipo de objeto. En el caso anterior, habría dos interruptores separados: uno para los objetos de tipo A y otro para los objetos de tipo B. La ventaja de esta configuración es que el subproceso principal no tiene que codificar el objeto en una matriz de bytes y el Los subprocesos lógicos de business less pueden compartir los mismos objetos que se usan en el disruptor (no se crea basura). La desventaja de esta configuración es que, de alguna manera, cada hilo de lógica de negocios tendrá que suscribirse a los mensajes de múltiples interruptores.

No intente este enfoque, probablemente necesite un EventProcessor personalizado que pueda sondear desde múltiples buffers de anillo.

Configure el disruptor para usar un solo tipo de objeto "super" que contenga todos los campos del mensaje A y B. Esto es muy contrario al estilo OO, pero permitirá un compromiso entre la opción # 1 y # 2. Configure el disruptor para usar referencias de objetos. Sin embargo, en este caso pierdo los beneficios de rendimiento de la preasignación de objetos y el orden de memoria.

Hemos hecho esto en un par de casos en los que es tolerable la falta de preasignación. Funciona bien Si está pasando objetos, debe asegurarse de anularlos una vez que haya terminado con ellos en el lado del consumidor. Descubrimos que el uso de un patrón de distribución doble para el objeto "super" mantuvo la implementación bastante limpia. Un inconveniente de esto es que obtendrás paradas de GC un poco más largas que con algo que era una matriz recta de objetos, ya que el GC tiene más objetos vivos para atravesar durante la fase de marca.

¿Qué recomiendas para esta situación? Siento que la opción # 2 es la solución más limpia, pero no sé si los consumidores pueden suscribirse técnicamente a los mensajes de varios disruptores o cómo. ¡Si alguien puede proporcionar un ejemplo de cómo implementar la opción # 2, sería muy apreciado!

Otra opción, si desea una flexibilidad total con respecto al uso de los datos, es no usar el búfer de anillo, sino hablar directamente con el secuenciador y definir el diseño de su objeto como mejor se vea.

Mi sistema tiene dos tipos diferentes de mensajes: tipo A y B. Cada mensaje tiene una estructura diferente: el tipo A contiene un miembro int y el tipo B contiene un miembro doble. Mi sistema necesita pasar ambos tipos de mensajes a numerosos hilos de lógica de negocios. Reducir la latencia es muy importante, así que estoy investigando el uso de un Disruptor para pasar mensajes desde el hilo principal a los hilos de la lógica de negocios de una manera mecánicamente comprensiva.

Mi problema es que el disruptor solo acepta un tipo de objeto en el búfer de anillo. Esto tiene sentido porque el disruptor asigna previamente los objetos en el búfer de anillo. Sin embargo, también hace que sea difícil pasar dos tipos diferentes de mensajes a mis hilos de lógica de negocios a través del Disruptor. De lo que puedo decir, tengo cuatro opciones:

  1. Configure el disruptor para usar objetos que contengan una matriz de bytes de tamaño fijo (según lo recomendado por ¿Cómo se debe usar Disruptor (Patrón de disruptor) para construir sistemas de mensajes del mundo real? ). En este caso, el hilo principal debe codificar los mensajes en matrices de bytes antes de publicarlos en el disruptor y cada una de las hebras de lógica de negocios debe decodificar los arrays de bytes en objetos al recibirlos. La desventaja de esta configuración es que los subprocesos de la lógica de negocios no están realmente compartiendo la memoria del disruptor, sino que están creando nuevos objetos (y, por lo tanto, creando basura) de la matriz de bytes proporcionada por el disruptor. La ventaja de esta configuración es que todos los subprocesos de lógica empresarial pueden leer varios tipos diferentes de mensajes del mismo disruptor.

  2. Configure el disruptor para usar un solo tipo de objeto, pero cree múltiples disruptores , uno para cada tipo de objeto. En el caso anterior, habría dos interruptores separados: uno para los objetos de tipo A y otro para los objetos de tipo B. La ventaja de esta configuración es que el subproceso principal no tiene que codificar el objeto en una matriz de bytes y el Los subprocesos lógicos de business less pueden compartir los mismos objetos que se usan en el disruptor (no se crea basura). La desventaja de esta configuración es que, de alguna manera, cada hilo de lógica de negocios tendrá que suscribirse a los mensajes de múltiples interruptores.

  3. Configure el disruptor para usar un solo tipo de objeto "super" que contenga todos los campos del mensaje A y B. Esto es muy contrario al estilo OO, pero permitirá un compromiso entre la opción # 1 y # 2.

  4. Configure el disruptor para usar referencias de objetos . Sin embargo, en este caso pierdo los beneficios de rendimiento de la preasignación de objetos y el orden de memoria.

¿Qué recomiendas para esta situación? Siento que la opción # 2 es la solución más limpia, pero no sé si los consumidores pueden suscribirse técnicamente a los mensajes de varios disruptores o cómo. ¡Si alguien puede proporcionar un ejemplo de cómo implementar la opción # 2, sería muy apreciado!


Ben Baumgold, estoy seguro de que ya has encontrado una solución. Su # 4 (o # 3) se puede implementar de forma trivial creando un titular de evento. Piense en ello como enumeración para los objetos. Para acelerar las búsquedas, los eventos deben enriquecerse con un tipo de enumeración. Aviso, estoy almacenando una referencia al evento original en el titular. Puede ser más apropiado crear un constructor de copia o clone () y copiar eventos al insertarlos en el búfer de anillo.

Ilustrando con el ejemplo:

// esto es una enumeración usada en eventos

public enum MyEventEnum { EVENT_TIMER, EVENT_MARKETDATA; }

// esto es titular. En cualquier momento, esta instancia en ringbuffer contiene solo un evento indexado por array [type.ordinal ()] . ¿Por qué matriz debe ser obvio desde el código.

public class RingBufferEventHolder { private MyEventEnum; private EventBase array[]; public RingBufferEventHolder() { array=new EventBase[MyEventEnum.values().length]; } // TODO: null the rest public void setEvent(EventBase event) { type=event.getType(); switch( event.getType() ) { case EVENT_TIMER: array[MyEventEnum.EVENT_TIMER.ordinal()]=event; break; case EVENT_MARKETDATA: array[MyEventEnum.EVENT_MARKETDATA.ordinal()]=event; break; default: throw new RuntimeException("Unknown event type " + event ); } }

// publicar evento

EventBase newEvent=new EventMarketData(....); // prepare long nextSequence = ringBuffer.next(); RingBufferEventHolder holder = ringBuffer.get(nextSequence); holder.setEvent(newEvent); // make the event available to EventProcessors ringBuffer.publish(nextSequence);