¿Estamos haciendo cola y serializando correctamente?

13

Procesamos los mensajes a través de una variedad de servicios (un mensaje tocará probablemente 9 servicios antes de que se haga, cada uno con una función específica relacionada con IO). En este momento tenemos una combinación del peor de los casos (serialización del contrato de datos XML) y el mejor de los casos (en memoria MSMQ) para el rendimiento.

La naturaleza del mensaje significa que nuestros datos serializados terminan entre 12 y 15 kilobytes, y procesamos aproximadamente 4 millones de mensajes por semana. Los mensajes persistentes en MSMQ fueron demasiado lentos para nosotros y, a medida que los datos crecen, sentimos la presión de los archivos de memoria de MSMQ. El servidor tiene un uso de memoria de 16 GB y crece, solo para poner en cola. El rendimiento también se ve afectado cuando el uso de la memoria es alto, a medida que la máquina comienza a intercambiarse. Ya estamos realizando el comportamiento de autolimpieza de MSMQ.

Siento que hay una parte que estamos haciendo mal aquí. Intenté usar RavenDB para conservar los mensajes y simplemente poner en cola un identificador, pero el rendimiento fue muy lento (1000 mensajes por minuto, como mucho). No estoy seguro de si eso es el resultado de usar la versión de desarrollo o qué, pero definitivamente necesitamos un rendimiento mayor [1]. El concepto funcionó muy bien en teoría, pero el rendimiento no estaba a la altura de la tarea.

El patrón de uso tiene un servicio que actúa como un enrutador, que hace todas las lecturas. Los otros servicios adjuntarán información basada en su enlace de terceros y se reenviarán al enrutador. La mayoría de los objetos se tocan de 9 a 12 veces, aunque alrededor del 10% se ven obligados a dar vueltas en este sistema durante un tiempo hasta que los terceros respondan de manera adecuada. Los servicios en este momento explican esto y tienen comportamientos adecuados para dormir, ya que utilizamos el campo de prioridad del mensaje por este motivo.

Entonces, mi pregunta, ¿cuál es una pila ideal para el paso de mensajes entre máquinas discretas pero con LAN en un entorno C # / Windows? Normalmente, comenzaría con BinaryFormatter en lugar de la serialización XML , pero eso es un agujero de conejo si una mejor manera es descargar la serialización a un almacén de documentos. Por lo tanto, mi pregunta.

[1]: La naturaleza de nuestro negocio significa que cuanto antes procesemos los mensajes, más dinero ganaremos. Hemos comprobado empíricamente que procesar un mensaje más adelante en la semana significa que tenemos menos probabilidades de ganar ese dinero. Si bien el rendimiento de "1000 por minuto" suena bastante rápido, realmente necesitamos ese número más de 10k / minuto. Solo porque estoy dando números en mensajes por semana no significa que tengamos una semana completa para procesar esos mensajes.

=============== editar:

Información adicional

Sobre la base de los comentarios, agregaré algunas aclaraciones:

  • No estoy seguro de que la serialización sea nuestro cuello de botella. He evaluado la aplicación y, si bien la serialización aparece en el gráfico de calor, solo es responsable de tal vez del 2,5 al 3% de la utilización de la CPU del servicio.

  • En general me preocupa la permanencia de nuestros mensajes y el posible uso indebido de MSMQ. Estamos utilizando mensajes no transaccionales y no persistentes para que podamos mantener el rendimiento en cola y me gustaría tener al menos mensajes persistentes para que sobrevivan a un reinicio.

  • Agregar más RAM es una medida provisional. La máquina ya ha pasado de 4 GB - > 16 GB de RAM y cada vez es más difícil eliminarlo para seguir agregando más.

  • Debido al patrón de enrutamiento en estrella de la aplicación, la mitad de las veces que se abre un objeto y luego se empuja a una cola, no cambia en absoluto. Esto se presta nuevamente (IMO) para almacenarlo en algún tipo de almacén de valor-clave en otro lugar y simplemente pasar identificadores de mensajes.

  • El patrón de enrutamiento en estrella es integral a la aplicación y no cambiará. No podemos centrarlo en la aplicación porque cada pieza a lo largo del camino funciona de forma asíncrona (en forma de sondeo) y queremos centralizar el comportamiento de reintento en un lugar.

  • La lógica de la aplicación está escrita en C #, los objetos son POCO inmutables, el entorno de implementación de destino es Windows Server 2012, y se nos permite instalar máquinas adicionales si una pieza de software en particular solo es compatible con Linux.

  • Mis objetivos son mantener el rendimiento actual al tiempo que reduce la huella de memoria y aumenta la tolerancia a fallas con un gasto mínimo de capital.

pregunta Bryan Boettcher 15.10.2013 - 21:21

3 respuestas

1

Aquí hay algunos puntos de referencia de la cola en los que podría estar interesado. MSMQ debería Ser capaz de manejar mensajes de 10K por segundo. ¿Podría tratarse de un problema de configuración o quizás los clientes no estén al día con la lectura de la cola? También tenga en cuenta lo increíblemente rápido que ZeroMQ está en esos puntos de referencia (alrededor de 100K mensajes por segundo), no ofrece una opción de persistencia, pero debería llevarlo a donde quiere que sea el rendimiento.

    
respondido por el stonemetal 25.10.2013 - 19:39
4

Hace algunos años tuvimos una situación similar, con un sistema de mensajes en cola (huellas digitales de audio en nuestro caso). Valoramos mucho la persistencia de los paquetes de datos en cola, pero descubrimos que poner en cola todo en el disco y consumir la cola del disco era muy costoso.

Si cambiamos a las colas basadas en memoria, el rendimiento fue excepcional, pero tuvimos un gran problema. De vez en cuando, los consumidores de las colas dejaron de estar disponibles durante un período de tiempo considerable (los elementos del consumidor y el productor en nuestro caso están conectados a través de WAN), por lo que la cola del productor crecería hasta un punto que se volvería inmanejable y como en su caso, una vez que el consumo de memoria fue muy alto, el exceso de memoria durante el intercambio llevó al sistema a un rastreo completo.

Diseñamos una cola que bautizamos como VMQueue (para Virtual Memory Queue, un nombre muy malo en retrospectiva). La idea de esta cola es que si el proceso del consumidor se está ejecutando a la par, en otras palabras, el procesamiento es lo suficientemente rápido para poder mantener el número de elementos en cola por debajo de un cierto nivel, entonces tiene básicamente el mismo rendimiento de una cola basada en la memoria. Sin embargo, cuando el consumidor se ralentiza o deja de estar disponible y la cola del productor crece hasta cierto tamaño, entonces la cola comenzará automáticamente a los elementos de paginación hacia y desde el disco (por cierto, utilizando la serialización BinaryFormatter ). Este proceso mantiene el uso de la memoria completamente controlado, y el proceso de paginación es rápido, o al menos mucho más rápido que el intercambio de memoria virtual que se produce durante la carga de memoria pesada. Una vez que el consumidor se las arregla para vaciar la cola por debajo del umbral, continúa trabajando como una cola basada en memoria pura

Si el sistema se bloquea o se reinicia, la cola puede recuperar todos los elementos paginados que se almacenaron en el disco, solo perderá los elementos que aún se guardaron en la memoria antes del bloqueo. Si puede permitirse perder un número limitado de paquetes durante un bloqueo o reinicio, esta cola puede ser útil.

Si estás interesado, puedo compartir el código fuente de la clase VMQueue para que puedas jugar con él. La cola aceptará cualquier clase que esté marcada como serializable. Al crear la cola, se establece el tamaño de la página en número de elementos. La interfaz de clase es virtualmente la misma que una clase de cola estándar. Sin embargo, el código es muy antiguo (.net 1.1), por lo que desafortunadamente no existe una interfaz genérica.

Sé que pasar de la tecnología MSMQ probada es una gran apuesta, sin embargo, esta cola ha estado funcionando de manera confiable durante casi 6 años y nos ha permitido sobrevivir y recuperarnos de escenarios en los que la máquina productora ha estado fuera de línea durante varias semanas. Por favor hazme saber si estas interesado. :)

    
respondido por el sgorozco 25.10.2013 - 01:07
1

sistema HP ProLiant ML350G5 obtiene 82k transacciones por minuto, es decir, tiene más de 8x ese rendimiento de "10k / minuto" que mencionaste.

  

Rendimiento: 82,774 tpmC

Además, para ser honesto, me hubiera ido con 64 o incluso 128 GB de RAM. La RAM es barata. Greenspun señala la diferencia entre "lanzar RAM en eso" y "conseguir que un chico inteligente educado en MIT lo optimice", y la RAM gana.

  

Terminó con una máquina SQL Server equipada con 64 GB de RAM y un puñado de máquinas front-end que ejecutan páginas ASP.NET ... El sitio, swaptree.com, maneja su membresía actual de más de 400,000 usuarios ( creciendo rápidamente) sin dificultad ...

La nota "la máquina ya ha pasado a 16 GB de RAM" está lejos de ser suficiente, con un artículo que señala un servidor que manejaba 400k usuarios con 64 GB de RAM.

    
respondido por el Marcel Popescu 25.10.2013 - 15:34

Lea otras preguntas en las etiquetas