Contexto del problema:
El día 1 del mes 9, alrededor de las 17:12, recibimos quejas del personal de implantación: algunos dispositivos no podían actualizarse con normalidad ni recibían las instrucciones enviadas desde la consola, entre otros problemas. Un compañero revisó los registros del proyecto control (en adelante, control) y descubrió que control no había recibido la información de sombra enviada por los dispositivos, por lo que no había enviado instrucciones. El proyecto control se conecta directamente con los dispositivos y, a partir de la información que estos comunican, les envía instrucciones y datos de configuración, incluidas actualizaciones y solicitudes de envío de registros. Antes de la puesta en marcha de IoT, control obtenía la información actual de los dispositivos mediante los informes de latido; después de la puesta en marcha de IoT, depende de la información de sombra de los dispositivos
para obtener la información actual del dispositivo. control se suscribe a la información de la sombra del dispositivo, pero es el servicio de sombras(abreviado IoT)el que se la reenvía; no recibe directamente los informes de sombra del dispositivo. Para conocer los detalles del flujo, consulta los dos diagramas siguientes:
La aplicación (control) obtiene el estado del dispositivo

La aplicación (control) envía instrucciones al dispositivo

Tras enterarme de que control no recibía los mensajes de sombra, fui inmediatamente a la consola de rabbitmq para comprobar si había mensajes y verificar dos cosas: 1.Si el dispositivo había enviado mensajes 2.Si rabbitmq funcionaba correctamente; las figuras 1 y 2 que aparecen a continuación son dos capturas de la consola de rabbitmq tomadas en aquel momento, a partir de Figura 1 Se puede determinar claramente que el dispositivo está enviando mensajes, pero muchos de ellos están en estado unacked (Esto indica que ya se ha entregado al consumidor, pero este aún no ha enviado el ack; en teoría, tras esperar un tiempo debería funcionar con normalidad), y para saber en qué cola se acumulan los mensajes unacked, consulta Figura 2,“spacebridgeiot-shadow” era precisamente lo que utilizábamos para recibir la información de sombra enviada por los dispositivos. Como los mensajes se habían acumulado en la cola, también era razonable que no se hubieran reenviado a control. Tras observarlo un tiempo, descubrimos que la cantidad de mensajes unacked había pasado a 0, pero el total apenas había cambiado. Daba la impresión de que los mensajes unacked habían vuelto a la cola para esperar su entrega. Efectivamente, unos minutos después volvimos a encontrar una gran cantidad de mensajes unacked; al cabo de unos minutos, estos mensajes regresaron a la cola, mientras control seguía sin recibirlos. Al revisar entonces los registros de IoT, descubrimos que no estaba entrando ningún mensaje de sombra. En la consola de rabbitmq, consultamos “spacebridgeiot-shadow” Resulta que ya no hay consumidores en esta cola, como Figura 3 como se muestra. Al consultar entonces los registros de rabbitmq, efectivamente aparecían mensajes de error, como Figura 4 muestra, rabbitmq cerró la conexión por iniciativa propia.
Figura 1:vista general de rabbitmq

Figura 2:gráfico de estadísticas de la cola de rabbitmq

Figura 3:Descripción general de spacebridgeiot-shadow

Figura 4: mensaje de error de rabbitmq

Solución provisional:
Como ya habían llegado muchas quejas, recurrimos a una solución bastante drástica: «eliminar los mensajes acumulados»; después de eliminarlos, todo volvió efectivamente a la normalidad(Nota: los problemas en producción deben resolverse lo antes posible; no tenemos tiempo para analizar los registros y resolverlos de manera ordenada, hay que actuar rápido)。

Reproducir el problema en un entorno local:
1.Acumular una gran cantidad de mensajes (al menos decenas de miles) en la cola «mirrorTestQueue» de rabbitmq del entorno 10.200.41.166
2.Detén los consumidores de mirrorTestQueue y reinícialos una vez que los mensajes hayan terminado de acumularse
3.Una vez completada la acumulación, reiniciar el consumidor
Tal como esperábamos, se enviaron miles de mensajes al consumidor en pocos segundos. Tras varios minutos, rabbitmq cerró por iniciativa propia la conexión con el consumidor y, en ese momento, el consumidor de la cola dejó de aparecer en la consola. Como habíamos configurado la recuperación automática del consumidor, este volvía a conectarse automáticamente al cabo de un rato,
Pero enseguida vuelve a desconectarse, prácticamente igual que el problema que encontramos en producción. ¿Qué lo estaba causando exactamente? Sinceramente, en aquel momento no tenía ninguna pista, y tras buscar por Internet tampoco encontré una respuesta especialmente satisfactoria(En aquel momento no había captado la esencia del problema y las palabras clave de búsqueda eran demasiado generales),más adelante
Sospechábamos que podía haber algún problema en la capa TCP, así que decidimos capturar paquetes para ver si encontrábamos alguna pista. Y, efectivamente, tuvimos suerte. Sin más preámbulos, vamos directamente a la imagen.


Antes de 13:06:25.643428, rabbitmq seguía enviando mensajes al consumidor. Al llegar a 13:06:25.643428, empezó a llenarse por completo la ventana TCP del consumidor. La situación duró unos 30 segundos, hasta que rabbitmq cerró activamente la conexión(envió un paquete RST). Después, el consumidor volvió a conectarse, la ventana volvió a llenarse y, tras unos 30 segundos más, se volvió a cerrar la conexión.


Parece seguir un patrón bastante regular, cada episodio dura 30s, lo que parece corresponder a un parámetro configurable; a grandes rasgos, se puede resumir así: “tcp full window hace que el servidor cierre activamente la conexión con un RST, y además sigue un patrón regular”
Esta vez cambié las palabras clave de búsqueda y encontré Respuesta,rabbitmq tiene un parámetro llamado tcp_listen_options.send_timeout es un parámetro que controla el tiempo de espera de escritura; cuando este se agota, se activa un RST de TCP(https://github.com/rabbitmq/rabbitmq-java-client/issues/341),modifícalo para probar el resultado:
1. Cambiar el tiempo de espera de escritura a 10s
tcp_listen_options.send_timeout = 10000
2.Capturar paquetes para comprobar si funciona


Desde que se llena la ventana hasta que se cierra la conexión transcurren unos 10s.
Revisión de lo ocurrido:
Como el consumidor de rabbitmq no tenía configurado prefetch, rabbitmq le entregó demasiados mensajes de una sola vez, lo que llenó la ventana TCP del consumidor y activó tcp_listen_options.send_timeout de rabbitmq. Cuando este tiempo de espera de escritura alcanza un umbral, rabbitmq desconecta la conexión TCP del consumidor.
Solución definitiva:
Eliminar mensajes había sido una solución de último recurso; aunque resolvió el problema, resultaba demasiado drástica. Necesitamos encontrar una solución elegante. Dado que el envío de demasiados mensajes al consumidor llenó la ventana TCP, deberíamos trabajar en la velocidad de recepción: basta con decirle a rabbitmq, al conectarnos, que no nos envíe tantos.(El siguiente pasaje está extraído de https://blog.csdn.net/james_searcher/article/details/70308565)rabbitmq tiene una propiedad llamada prefetch, que indica el número máximo de mensajes sin confirmar que puede tener un único consumidor. ¿Cómo se interpreta? mq configura un búfer para cada consumidor, cuyo tamaño viene determinado por prefetch. Cada vez que recibe un mensaje, MQ lo envía al búfer y después al cliente. Cuando recibe una confirmación ack (el consumidor emite la instrucción baseack), mq libera una posición del búfer y añade un nuevo mensaje. Sin embargo, si el búfer está lleno en ese momento, MQ entra en un estado de bloqueo. Más concretamente, supongamos que prefetch se establece en 10 y hay dos consumidores. Esto significa que cada consumidor obtiene por adelantado 10 mensajes de la cola y los almacena localmente a la espera de procesarlos. Al mismo tiempo, el número de mensajes sin confirmar del canal pasa a ser 20. Rabbit los entrega en este orden:primero entrega 10 mensajes a consumer1 y después 10 mensajes a consumer2. Si en ese momento hay un nuevo mensaje pendiente de entrega, primero comprueba si el número de mensajes sin confirmar del canal es igual a 20; si lo es, no entrega el mensaje a ningún consumidor y este permanece en la cola. Después, cuando uno de los consumidores confirma un mensaje mediante ack, el número de mensajes sin confirmar pasa a ser 19. Entonces Rabbit comprueba qué consumidor tiene menos de 10 mensajes sin confirmar y se lo entrega.
En el código, concretamente, sería

¿Cómo se evalúa este valor? Hay un artículo oficial de rabbitmq que lo explica muy bien, así que no entraré en detalles. En nuestro sistema, actualmente está establecido en 20.
https://www.rabbitmq.com/blog/2012/05/11/some-queuing-theory-throughput-latency-and-bandwidth/
Palabras finales:
Todavía somos principiantes en el uso de rabbitmq; antes de utilizarlo, es imprescindible consultar detenidamente su API, familiarizarse con el uso de las API habituales y realizar numerosas pruebas en un entorno local.