Arresto di sistemi asincroni

2

Ho un sistema che ha 5 servizi. Ciascuno viene eseguito in un processo separato. Assomigliano a una catena di montaggio, nel senso che per lo più elaborano i messaggi nello stesso ordine 1- > 2- > 3- > 4- > 5, tuttavia non è sempre il caso che l'elaborazione sia lineare. Ci sono casi in cui è possibile saltare la catena (ad es. 1- > 2- > 5) o andare indietro (ad es. 1- > 2- > 4- > 3- > 1) o anche a zig-zag (1- > 2- > 4- > 3- > 5).

Il sistema viene alimentato con i payload per elaborarli uno ad uno, iniziando con il servizio 1. Dato che il sistema è asincrono e non lineare, non posso sapere quando l'intero sistema ha elaborato ciascuno dei payload. D'altra parte, voglio che il sistema si fermi a volte e la scelta migliore è non appena ha elaborato tutti i payload.

Queste soluzioni non sarebbero troppo buone:

  • Una semplice uccisione o l'utilizzo di un approccio con pillola velenosa non funzionerebbe (esempio link ), come allora potrei finire per non elaborare il payload completamente (1- > 2- > 1 fallirebbe, dato che 1 sarebbe stato fermato dopo che 2 aveva ricevuto l'ultimo payload)
  • Posso eseguire un approccio di timeout, tuttavia ciò non funzionerebbe per due motivi: a) ostacolerà lo sviluppo / test (ad es. se ho aspettato 1 minuto per un tempo di elaborazione effettivo di 5 secondi), b) i timeout possono ancora essere troppo basso, quindi potrei incorrere in problemi se alcuni servizi non hanno finito l'elaborazione

Stavo pensando di creare un altro servizio che servisse come un tipo di arresto a due fasi. L'idea andrebbe su queste linee:

  • Qualsiasi servizio potrebbe chiamare stopper per dire "Voglio che il sistema si fermi"
  • Stopper chiamerebbe tutti gli altri servizi e dir loro "Stop è stato richiesto"
  • Gli altri servizi attenderanno fino a quando non elaboreranno ciò che hanno in coda e diranno stopper "Posso essere fermato"
  • Il fermo attenderà che raccolga "Posso essere fermato" da tutti i servizi, quindi trasmetti "Esegui stop"
  • Tutti i servizi andranno avanti e interrompono i rispettivi processi in modo aggraziato

Ho problemi con quanto sopra:

  • Sembra eccessivamente complesso per il vantaggio (rispetto a "aspetta solo 1 minuto e poi interrompi" e non aggiunge alcun codice non commerciale), non sono sicuro che valga la pena di bilanciare
  • Dovrei infondere logica legata all'arresto in tutti i servizi e dovrebbe essere accoppiato al meccanismo di accodamento (quindi sa quando non ci sono più messaggi di carico utile) e alla logica di business (quindi sa quando il carico utile stesso è finito l'elaborazione)
  • Ci sono ancora alcuni casi limite da considerare. Per esempio. in 1- > 2- > 1 scenario, 1 potrebbe finire, 2 potrebbe elaborare l'ultimo payload, quando è fatto potrebbe inviare un messaggio a 1 e per fermare il servizio, stopper invierà "Esegui stop" e 1 potrebbe ricevere quel messaggio prima che ricevesse un messaggio da 2 che dovrebbe elaborare, quindi potrebbe fermarsi prematuramente

Ci sono buone pratiche o esempi su come ottenere questo in qualche altro modo? O forse limitando il sistema in qualche altro modo che renderebbe questo problema scomparire / più facile da gestire?

    
posta steady rain 23.02.2015 - 23:42
fonte

3 risposte

2

Basta avere un messaggio di controllo "STOP".

Invia a processare 1,

Il processo 1 dovrebbe inoltrare un messaggio di STOP a tutti gli altri processi.

Ogni processo alla ricezione di un messaggio di STOP dovrebbe scaricare tutti i messaggi dalla sua coda, attendere che altri messaggi appaiano sulla coda (20 secondi circa) e se non arrivano più messaggi in quel momento, spegnere altrimenti elaborerà i messaggi rimanenti e aspetta altri 20 secondi.

Sarà necessario un meccanismo per differenziare un messaggio "nuovo" genuinamente dall'esterno del sistema che arriva al processo 1 (che non si desidera elaborare) e i messaggi generati internamente dai processi 2-5 che si desidera elaborare prima di chiudere verso il basso.

    
risposta data 27.03.2015 - 10:43
fonte
1

L'ultima volta che ho dovuto fare questo, ho progettato i passaggi nella catena di montaggio in modo che, se qualcuno dei passi avesse ricevuto un oggetto nullo dal passaggio precedente, ciò indicava che non c'erano più elementi da elaborare.

Quindi tutto ciò che devi fare è semplicemente inviare un segnale di stop all'oggetto che sta alimentando le linee di assemblaggio. L'oggetto invierà quindi elementi null alle linee di assemblaggio, eliminandoli in modo efficace e chiudendoli. Quando ciascun passaggio si arresta, invia un oggetto nullo al passaggio successivo, spegnendolo anch'esso.

Quindi ecco l'architettura della mia ultima applicazione:

File Reader --> Producer  --> Queue --> Filter --> --> Output Queue --> Consumer --> File Writer
                          \-> Queue --> Filter --> -/

Il produttore divide i pacchetti di file di input in canali, rappresentati da più combinazioni di filtri di coda (pipeline). Il filtro è conforme alle interfacce di input e output, rendendolo intercambiabile. L'intera architettura dal produttore al consumatore (ma escludendo il lettore di file e gli oggetti del file writer) è racchiusa in una classe Controller. Ogni combinazione coda / filtro è contenuta in una classe Pipeline. La classe Controller ha un metodo Cancel, che invia un segnale di annullamento al produttore. Il produttore, a sua volta, chiama i metodi Cancel su ciascuna delle pipeline. Esiste un metodo Flush () su ciascuna coda, chiamata dagli oggetti pipeline.

Quando Dequeue () viene chiamato su una coda, farà una delle tre cose:

  1. Restituisce un pacchetto dalla coda o
  2. Se la coda è vuota, bloccare fino a quando un pacchetto non viene messo in coda, a meno che
  3. Flush () è stato chiamato in coda, nel qual caso il metodo Dequeue () restituirà un oggetto nullo se la coda è vuota.

Quindi quando non ci sono più pacchetti disponibili, il filtro vedrà un pacchetto nullo. L'intero processo viene completato quando la coda di output restituisce un oggetto nullo dopo che è stato scaricato. Il controller non svuota la coda di output fino a quando tutte le pipeline non hanno segnalato che sono entrambe svuotate e vuote (dopo aver ricevuto l'ultimo oggetto nullo dalla coda di input).

Ora, se esiste una condizione di loop, in cui un oggetto ritorna ad uno stadio precedente, ciò che devi fare è avvolgere il tuo pacchetto in un altro oggetto, in modo che tu possa memorizzare alcune informazioni di stato con il pacchetto, come ad esempio molte volte è passato alla fase 1, ecc.

    
risposta data 24.02.2015 - 01:38
fonte
0

Se disponi di una coda circolare di servizi (ad es. 1 - > 2 - > 1) in modo da non poter inviare alcuna forma di messaggio "stop", dovrai interrompere la registrazione all'intero sistema e attendere tutti loro per completare l'elaborazione.

Solo quando sono tutti tranquilli sarà sicuro spegnerli tutti, quindi avrai bisogno di un monitor per vedere lo stato di elaborazione di ogni servizio. In alternativa potrebbe essere possibile contare le voci e uscire dal sistema se esce ogni messaggio inviato. Quindi è possibile interrompere l'accesso al sistema e l'ultimo processo nella catena (uno speciale che è sempre posizionato per ultimo ed esegue una piccola elaborazione, magari solo una segnalazione) è quello che può dire quando il sistema ha completato tutta l'elaborazione - se questo è appropriato per il tuo sistema.

    
risposta data 24.02.2015 - 11:55
fonte

Leggi altre domande sui tag