Vai al contenuto principale

Cerca su CooLWJ

选择搜索范围,输入关键词后按回车。

Lingua

I consumer di rabbitmq «scompaiono senza motivo»

Contesto del problema:

Il giorno 1 del mese 9, intorno alle 17:12, abbiamo ricevuto segnalazioni dagli addetti all’implementazione: alcuni dispositivi non riuscivano ad aggiornarsi correttamente, non ricevevano i comandi inviati dalla console e presentavano altri problemi. Un collega ha controllato i log del progetto control (di seguito abbreviato in control) e ha scoperto che control non aveva ricevuto le informazioni shadow trasmesse dai dispositivi, perciò non aveva inviato i comandi. Il progetto control si interfaccia direttamente con i dispositivi e, sulla base delle informazioni da essi trasmesse, invia comandi e configurazioni, tra cui aggiornamenti e invio dei log. Prima della messa in produzione di IoT, control ricavava le informazioni correnti dei dispositivi dai messaggi heartbeat; dopo la messa in produzione di IoT, si basa sulle informazioni shadow dei dispositivi

per ottenere le informazioni attuali del dispositivo, control si iscrive agli aggiornamenti dello stato ombra del dispositivo, ma tali informazioni vengono inoltrate dal servizio ombra (abbreviato IoT); non riceve direttamente i rapporti sullo stato ombra inviati dal dispositivo. Per i dettagli del flusso, consulta i due diagrammi qui sotto:

L'applicazione (control) acquisisce lo stato del dispositivo

L'applicazione (control) invia comandi al dispositivo

 

Dopo aver saputo che control non riceveva i messaggi shadow, sono andato immediatamente nella console di rabbitmq a verificare la presenza di messaggi e ad accertare due cose: 1. se il dispositivo avesse inviato messaggi 2. se rabbitmq funzionasse normalmente; le figure 1、2 qui sotto sono due schermate della console di rabbitmq acquisite in quel momento, da Figura 1 Si può stabilire chiaramente che il dispositivo sta inviando messaggi, ma molti di essi sono unacked(indica che è già stato consegnato al consumatore, che semplicemente non ha ancora inviato l’ack; in teoria, attendendo un po’, dovrebbe tornare tutto normale); per sapere in quale coda si accumulano i messaggi unacked, vedere Figura 2,“spacebridgeiot-shadow” è proprio quello che usiamo per ricevere le informazioni sullo stato ombra inviate dai dispositivi. I messaggi si erano tutti accumulati nella coda, quindi era comprensibile che non fossero stati inoltrati a control. Dopo aver osservato per un po', abbiamo scoperto che il numero di unacked era diventato 0, mentre il totale indicato da total non era cambiato molto. Sembrava che i messaggi unacked fossero tornati nella coda in attesa di essere consegnati. Infatti, dopo qualche minuto abbiamo nuovamente rilevato molti messaggi unacked; trascorso qualche altro minuto, questi messaggi unacked sono tornati nella coda, mentre control continuava a non ricevere messaggi. A quel punto, controllando i log di IoT, abbiamo scoperto che non arrivava alcun messaggio ombra. Nella console di rabbitmq abbiamo controllato “spacebridgeiot-shadow” Inaspettatamente, questa coda non ha più consumatori, come Figura 3 come mostrato. A questo punto, controllando i log di rabbitmq, si trovano effettivamente messaggi di errore, come Figura 4 come mostrato, rabbitmq ha chiuso attivamente la connessione.

Figura 1:panoramica di rabbitmq

 

 

Figura 2:grafico delle statistiche delle code di rabbitmq

 

 

Figura 3:Panoramica di spacebridgeiot-shadow

 

 

 Figura 4:messaggio di errore di rabbitmq

 

Soluzione temporanea:

Dato che erano già arrivati molti reclami, abbiamo adottato una soluzione piuttosto drastica: “eliminare i messaggi accumulati”. Dopo l’eliminazione, tutto è effettivamente tornato alla normalità(Nota: i problemi in produzione devono essere risolti il prima possibile; non abbiamo il tempo di analizzare i log e poi procedere con ordine alla soluzione, bisogna fare in fretta)。

 

Identificatore tecnico di RabbitMQ

Riprodurre il problema in un ambiente offline:

1.Accumulare un gran numero di messaggi (almeno nell'ordine delle decine di migliaia) nella coda RabbitMQ “mirrorTestQueue” dell'ambiente 10.200.41.166

2.Arrestare il consumatore di mirrorTestQueue e riavviarlo una volta terminato l'accumulo

3.Completato l'accumulo, riavviare il consumer

 

Come previsto, in pochi secondi vengono inviati al consumer migliaia di messaggi;dopo alcuni minuti rabbitmq chiude autonomamente la connessione con il consumer, che a quel punto non compare più nella console tra i consumer della coda. Poiché abbiamo configurato il ripristino automatico del consumer, dopo un po' si riconnette automaticamente,

Ma poco dopo la connessione si interrompe di nuovo, praticamente come nel problema riscontrato nel nostro ambiente di produzione: che cosa lo provoca esattamente? A dire il vero, allora non avevo idee e, dopo aver cercato un po’ in rete, non avevo trovato risposte particolarmente soddisfacenti(Allora non avevo colto la natura del problema e le parole chiave usate per la ricerca erano troppo generiche),in seguito

Sospettavamo che ci fosse un problema a livello TCP, quindi abbiamo deciso di catturare i pacchetti per vedere se riuscivamo a trovare qualche indizio. E infatti la fortuna ci ha assistito: senza aggiungere altro, ecco l'immagine.

 

 

 Prima delle 13:06:25.643428, rabbitmq continuava a inviare messaggi al consumatore; alle 13:06:25.643428, la finestra TCP del consumatore ha iniziato a saturarsi. Dopo circa 30 secondi, rabbitmq ha interrotto attivamente la connessione (inviando un pacchetto RST). Il consumatore si è poi riconnesso, ma la finestra ha continuato a saturarsi e, dopo altri 30 secondi circa, la connessione è stata nuovamente interrotta.

 

Sembra piuttosto regolare: ogni volta dura 30s. Sembra un parametro configurabile. Riassumendo a grandi linee: “tcp full window ha indotto il server a chiudere attivamente la connessione con un RST, e il fenomeno si ripete con regolarità”

Questa volta, cambiando le parole chiave della ricerca, ho trovato Risposta,rabbitmq ha un parametro chiamato tcp_listen_options.send_timeout è un parametro che controlla il timeout di scrittura; quando la scrittura supera il tempo limite, viene attivato un RST TCP(https://github.com/rabbitmq/rabbitmq-java-client/issues/341),proviamo a modificarlo per vedere il risultato:

1. Impostare il timeout di scrittura a 10s

tcp_listen_options.send_timeout = 10000

 2.Catturare i pacchetti per verificare se funziona

 

 Dal riempimento della finestra alla chiusura della connessione trascorrono circa 10s.

 

Ricostruzione del fenomeno:

Poiché sul consumatore di rabbitmq non era stato impostato prefetch, rabbitmq ha consegnato troppi messaggi in una sola volta, riempiendo la finestra TCP del consumatore e attivando così tcp_listen_options.send_timeout di rabbitmq. Quando questo timeout di scrittura raggiunge una determinata soglia, rabbitmq interrompe la connessione TCP con il consumatore.

 

Soluzione definitiva:

        In precedenza, eliminare i messaggi era stata una soluzione obbligata: risolveva il problema, ma in modo troppo drastico. Dobbiamo trovare una soluzione elegante. Dato che la finestra TCP si riempie perché vengono inviati troppi messaggi ai consumer, dovremmo intervenire sulla velocità di ricezione: basta dire a rabbitmq, quando ci colleghiamo, di non inviarcene troppi.(Il brano seguente è tratto da https://blog.csdn.net/james_searcher/article/details/70308565)rabbitmq ha un attributo chiamato prefetch, che indica il numero massimo di messaggi senza conferma che un singolo consumatore può ricevere. Come va interpretato? mq assegna a ogni consumer un buffer, la cui dimensione è pari a prefetch. Ogni volta che riceve un messaggio, MQ lo inserisce nel buffer e poi lo inoltra al client. Quando riceve un messaggio ack (il consumer invia il comando baseack), mq libera una posizione nel buffer e aggiunge un nuovo messaggio. Se però il buffer è pieno, MQ entra in uno stato di blocco. Più precisamente, supponiamo che prefetch sia impostato a 10 e che vi siano due consumer. Ciò significa che ogni consumer preleva anticipatamente dalla queue 10 messaggi per volta e li conserva nella cache locale in attesa di elaborarli. Contemporaneamente, il numero di messaggi unacked del channel diventa 20. L’ordine di consegna di Rabbit è il seguente: prima consegna a consumer1 tutti i 10 messaggi, poi ne consegna 10 a consumer2. Se a questo punto occorre consegnare un nuovo messaggio, verifica prima se il numero di messaggi unacked del channel è pari a 20; in tal caso il messaggio non viene consegnato ai consumer e rimane nella queue. Successivamente, quando uno dei consumer conferma un messaggio con un ack, il numero di messaggi unacked scende a 19; Rabbit verifica quindi quale consumer abbia meno di 10 messaggi unacked e gli consegna il messaggio.

 

Nel codice, concretamente, si traduce in

 

 

Come valutare questo valore? Un articolo ufficiale di rabbitmq lo spiega molto bene, quindi non entrerò nei dettagli; attualmente nel nostro sistema è impostato a 20.

https://www.rabbitmq.com/blog/2012/05/11/some-queuing-theory-throughput-latency-and-bandwidth/

 

 

Conclusione:

Siamo ancora principianti nell'uso di rabbitmq; prima di utilizzarlo, è indispensabile leggere attentamente le API di rabbitmq, familiarizzare con l'uso delle API più comuni e fare molti esperimenti nell'ambiente locale.


Commenti

Invio in corso

Scegli il tema

Il tema si applica a tutto il sito.