Esecuzione e sincronizzazione di lavori remoti

2

Ho un'infrastruttura per master e nodi. Master esegue lavori remoti sui nodi. Ogni lavoro restituisce il messaggio di successo / fallimento al master. Quindi il flusso di esecuzione è il seguente:

  1. master riceve il comando
  2. master crea un lavoro remoto e invia un messaggio al nodo per eseguire il lavoro remoto
  3. master restituisce "accettato"
  4. il lavoro remoto è in esecuzione sul nodo
  5. il lavoro remoto invia un messaggio per informare il cliente del risultato

Cosa succede se il master riceve un nuovo comando sulla stessa risorsa a volte durante il 4, cioè mentre il lavoro remoto è in esecuzione?

Ho 2 opzioni:

  1. Rifiuta il nuovo comando. Tuttavia, devo assicurarmi che il lavoro remoto ritorni sempre e, in caso contrario, un certo timeout fallirà il comando - fondamentalmente, per impedire che la risorsa sia bloccata per sempre da qualche attività di stallo.

  2. Accetta il nuovo comando e lo accoda. Al termine della prima attività sulla risorsa, eseguiamo la successiva. Tuttavia, questa soluzione è più complessa, poiché una catena di comandi potrebbe fallire nel mezzo; necessità di tenere traccia delle attività future ecc.

La mia principale preoccupazione è l'alta disponibilità del sistema, per essere più robusto e quindi super-potente. Ovviamente, mi sto appoggiando alla soluzione n. 1.

C'è qualcosa che mi manca qui? Qualche altro approccio è migliore?

Modifica

Esempio più concreto: 1 master, 1 nodo. Master può ricevere un messaggio: create , update e delete . Ogni messaggio viene eseguito su un nodo come lavoro remoto. Questi lavori creano, aggiornano ed eliminano alcune risorse sui nodi. Quindi voglio impedire ad es. per eseguire delete / update mentre la risorsa viene creata e situazioni simili.

L'ordine di esecuzione dei comandi, ovviamente, deve essere uguale a quello ricevuto.

    
posta igor 28.02.2016 - 16:01
fonte

1 risposta

2

Anche per il tuo scenario a 1 nodo, se vuoi che quanto sopra funzioni correttamente in caso di guasto della macchina / rete, allora quello che stai chiedendo è di risolvere consenso . Questo è impossibile da raggiungere con il significato tecnico di "disponibilità". Questo è il teorema CAP . (Attenzione alla dicitura fuorviante che descrive la "disponibilità" in quella pagina. Una formulazione migliore è: "qualsiasi richiesta valida riceverà una risposta di successo non importa quale ". il sistema non è disponibile se restituisce un messaggio di errore per ogni richiesta o, in effetti, qualsiasi richiesta valida. Una richiesta non valida, come la lettura di una chiave che non esiste, può ovviamente avere una risposta di errore. ) Per "alta disponibilità", tuttavia, probabilmente si intende qualcosa del tipo "funzionerà anche se i server N non funzionano". Questo è realizzabile ed è ciò che fanno algoritmi come Paxos, Viewstamped Replication e Raft.

Detto questo, davvero non vuoi implementare questi algoritmi per l'uso di produzione. È molto facile fare errori molto sottili quando li si implementa. Fortunatamente, altri hanno già realizzato implementazioni pronte per la produzione. In particolare, consiglio di utilizzare Apache ZooKeeper , o possibilmente Apache Kafka (che è stato costruito in cima a ZooKeeper). Se decidi di utilizzare ZooKeeper "direttamente", probabilmente vorrai dare un'occhiata a Apache Curator .

Se le tue operazioni sono idempotenti (e sembra proprio che possano essere) e i lavori degli ordini siano eseguiti su nodi diversi non importa, allora avere il master essere un cluster Kafka e avere i nodi sottoscrivere argomenti indipendenti gestirà questo graziosamente con una buona efficienza. Basta impegnare l'offset dopo aver gestito ogni richiesta. Se l'ordine è importante tra i nodi, è possibile utilizzare un singolo argomento a cui tutti i nodi si iscrivono per ottenere le richieste. Quindi tutti i nodi inviano le loro risposte a un argomento Kafka separato. Infine, tutti i nodi consumano anche l'argomento della risposta e elaborano solo la richiesta successiva quando viene utilizzata una risposta alla richiesta precedente. Le risposte duplicate possono essere facilmente ignorate e compattate.

Per ottenere esattamente una volta la semantica è necessario essere in grado di commettere atomicamente l'offset del log con qualsiasi stato persistente del nodo. Se lo stato del tuo nodo è una funzione deterministica della cronologia delle richieste ricevute, allora una volta la semantica è banale: tutte le informazioni necessarie sono nel registro di Kafka, basta riprodurle. Questo scenario è estremamente desiderabile, si adatta molto naturalmente a Kafka e semplifica notevolmente le cose. In questo scenario, la replica del nodo è banale; puoi semplicemente eseguire più copie, puoi girarle ogni volta e ricreare il loro stato, non è necessario alcun coordinamento tra loro.

Se lo stato dei nodi non è una funzione deterministica della cronologia delle richieste che riceve, allora ci si trova in una posizione potenzialmente pelosa che consiste essenzialmente nell'implementare un registro di replica. La buona notizia è che ZooKeeper e Kafka sono in grado di gestire molti degli aspetti più complicati. Puoi utilizzare ZooKeeper per eleggere un leader e Kafka per archiviare e distribuire il registro di replica. Il leader consumerà il registro delle richieste, eseguirà qualsiasi azione non deterministica come leggere l'orologio locale o fare richieste web ad altri server, calcolare un delta tra lo stato corrente e il nuovo stato provvisorio, quindi pubblicare quel delta, che può essere applicato in modo deterministico a un argomento di Kafka che rappresenta il registro di replica. I nodi (entrambi i follower e il leader) quindi utilizzano il registro di replica applicando i delta al loro stato locale. Il leader pubblica la risposta all'argomento appropriato quando applica il delta che legge dal log di replica. È possibile che vengano pubblicati più delta per la stessa richiesta in caso di modifica della leadership durante l'elaborazione. Questo non è un problema poiché il log di replica è rigorosamente ordinato e qualsiasi delta dopo il primo può essere ignorato. (Nello scenario del paragrafo precedente, il log di richiesta era il log di replica che è il motivo per cui tanto è stato semplificato.)

L'ultima preoccupazione è che probabilmente vorrai fare una qualche forma di checkpoint in modo da non dover mantenere il log completo e riprodurlo dall'inizio. Un'altra cosa bella di questo design incentrato sul log è che questo può essere fatto come un processo in background se lo si desidera.

    
risposta data 29.02.2016 - 00:41
fonte

Leggi altre domande sui tag