Quali sono i modelli di progettazione maggiormente sfruttati nella creazione di applicazioni ad alta disponibilità? [chiuso]

StackOverflow https://stackoverflow.com/questions/814742

Domanda

Allo stesso modo ci sono schemi di progettazione che dovrebbero essere evitati?

È stato utile?

Soluzione

Suppongo che tu stia scrivendo un'applicazione di tipo server (lasciamo un po 'di app Web - ci sono alcune buone soluzioni standard che possono aiutarti lì, quindi diamo un'occhiata a " ho questo fantastico nuovo tipo di server Ho scritto " ;, ma voglio che sia un problema HA).

In un'implementazione del server, le richieste dai client vengono generalmente (in una forma o nell'altra) convertite in un modello di evento o tipo di comando e quindi eseguite su una o più code.

Quindi, primo problema: è necessario memorizzare eventi / comandi in un modo che sopravviva nel cluster (cioè quando un nuovo nodo prende il posto di master, osserva il comando successivo che deve essere eseguito e inizia).

Cominciamo con un singolo impl server thread (il più semplice - e i concetti si applicano ancora al multi-thread ma ha il suo set di problemi0. Quando un comando viene elaborato richiede una sorta di elaborazione delle transazioni.

Un'altra preoccupazione è la gestione degli effetti collaterali e come gestite il fallimento del comando corrente? Ove possibile, gestire gli effetti collaterali in modo transazionale, in modo che siano tutti o niente. vale a dire. se il comando modifica le variabili di stato, ma si blocca a metà dell'esecuzione, potendo tornare al "precedente" lo stato è fantastico. Ciò consente al nuovo nodo master di riprendere il comando bloccato e di eseguire nuovamente il comando. Un buon modo è di nuovo la suddivisione degli effetti collaterali in attività più piccole che possono essere nuovamente eseguite su qualsiasi nodo. vale a dire. memorizzare le attività di inizio e fine della richiesta principale, con molte piccole attività che gestiscono, per esempio, solo un effetto collaterale per attività.

Questo introduce anche altri problemi che influenzeranno il tuo design. Tali variabili di stato non sono necessariamente aggiornamenti dei database. Potrebbero essere stati condivisi (per esempio una macchina a stati finiti per un componente interno) che devono anche essere distribuiti nel cluster. Pertanto, il modello per la gestione delle modifiche è tale che il codice principale deve visualizzare una versione coerente dello stato di cui ha bisogno e quindi eseguire il commit di tale stato nel cluster. È utile utilizzare una forma di archiviazione dei dati immutabile (almeno dal thread principale che esegue l'aggiornamento). vale a dire. tutti gli aggiornamenti vengono effettivamente eseguiti su nuove copie che devono passare attraverso una sorta di mediatore o facciata che aggiorna solo il locale nelle copie di memoria con gli aggiornamenti dopo l'aggiornamento attraverso il cluster (o il numero minimo di membri attraverso il cluster per coerenza dei dati).

Alcuni di questi problemi sono presenti anche per i sistemi master worker.

È inoltre necessaria una buona gestione degli errori poiché aumenta il numero di cose che possono andare storte nell'aggiornamento dello stato (poiché ora è coinvolta la rete).

Uso molto il modello di stato. Invece di aggiornamenti di una riga, per gli effetti collaterali si desidera inviare richieste / risposte e utilizzare gli sms specifici della conversazione per tenere traccia dell'avanzamento.

Un altro problema è la rappresentazione degli end point. vale a dire. il client connesso al nodo principale deve essere in grado di riconnettersi al nuovo master e quindi attendere i risultati? O semplicemente annulli tutti i risultati in sospeso e consenti ai clienti di inviare nuovamente? Se si consente l'elaborazione di richieste in sospeso, è necessario un modo semplice per identificare gli endpoint (client) (ad es. Una sorta di ID client in una ricerca).

È inoltre necessario il codice di pulizia, ecc. (ad es. non si desidera che i dati in attesa che un client si riconnetta per attendere per sempre).

Vengono utilizzate molte code. Molte persone useranno quindi alcuni bus di messaggi (jms dire per java) per spingere gli eventi in modo transazionale.

La terracotta (di nuovo per java) risolve molto di tutto questo per te - aggiorna la memoria - qui la terracotta è la tua facciata / mediatore. Hanno appena iniettato gli aspetti per il tuo.

Terracotta (non lavoro per loro) - introduce il concetto di "super statico", in modo da ottenere questi singoli cluster ampi che sono interessanti, ma devi solo essere consapevole di come ciò influirà sul flusso di lavoro di test e sviluppo - ie. usare molta composizione, anziché ereditare implementazioni concrete per un buon riutilizzo.

Per le app Web: un buon server di app può aiutare con la replica delle variabili di sessione e un buon bilanciamento del carico funziona. In qualche modo, usarlo tramite un REST (o il tuo metodo di servizio web preferito) è un modo semplice per scrivere un servizio multi-thread. Ma avrà implicazioni sulle prestazioni. Ancora una volta dipende dal tuo dominio problematico.

I servizi di messaggi (diciamo jms) vengono spesso utilizzati per introdurre un accoppiamento libero tra servizi diversi. Con un server di messaggi decente, puoi fare molti routing di msg (di nuovo Apache Camel o simili fa un ottimo lavoro). dire un consumatore appiccicoso contro un gruppo di produttori di jms ecc. che può anche consentire un buon failover. Le code ecc. Di Jms possono fornire un modo semplice per distribuire cmds nel cluster, indetti di master / slave. (di nuovo dipende se stai facendo LOB o scrivendo un server / prodotto da zero).

(se avrò tempo dopo riordinerò, forse aggiungerò qualche dettaglio in più nella grammatica dell'ortografia, ecc.)

Altri suggerimenti

Un approccio alla creazione di software affidabile è software solo crash :

  

Il software solo per crash è un software che si arresta in modo sicuro e si ripristina rapidamente. L'unico modo per fermarlo è bloccarlo e l'unico modo per avviarlo è ripristinarlo. Un sistema solo crash è composto da componenti solo crash che comunicano con richieste ripetibili; gli errori vengono gestiti arrestando in modo anomalo e riavviando il componente difettoso e ritentando eventuali richieste scadute. Il sistema risultante è spesso più robusto e affidabile perché il ripristino di emergenza è un cittadino di prima classe nel processo di sviluppo, piuttosto che un ripensamento, e non è più necessario il codice aggiuntivo (e interfacce e bug associati) per l'arresto esplicito. Tutti i software dovrebbero essere in grado di arrestarsi in modo sicuro e ripristinarsi rapidamente, ma il software solo per crash deve avere queste qualità o la loro mancanza diventa rapidamente evidente.

Consiglio di leggere Rilascialo! di Michael Nygard. Descrive una serie di anti-schemi che incidono sui sistemi di produzione e schemi per aiutare a impedire a un componente errato di abbattere l'intero sistema. Il libro copre tre aree principali; Stabilità, capacità e progettazione generale (riguardanti reti, sicurezza, disponibilità e amministrazione).

Il mio posto di lavoro precedente è stato morso (una volta o l'altra) praticamente da ogni singolo scenario di fallimento che Nygard delinea (con perdita di entrate per ogni interruzione risultante). L'implementazione di alcune delle tecniche e dei modelli che suggerisce ha portato a sistemi significativamente più stabili e prevedibili (e sì, il libro è un po 'incentrato su Java, ma i principi sono applicabili in molti contesti).

Sbagliato:

  

... e ci sarà un server di archiviazione

Buono:

  

... e ci sarà una farm di (multipli) di archiviazione   server con (più) bilanciatori di carico davanti   di loro

  • Metti i bilanciatori di carico davanti a tutto. Per ora puoi avere 4 backend, ma in futuro puoi averne 400, quindi è saggio gestirlo solo su LB, non tutte le app che usano il backend.

  • Utilizza più livelli di cache.

  • Cerca soluzioni popolari per accelerare i colpi (memcached per esempio).

  • Se si intende rinnovare un sistema, farlo parte per parte, in più piccoli passaggi. Se lo fai in un unico grande passo (spegni quello vecchio, accendi quello nuovo e prega che funzionerà) molto probabilmente fallirà.

  • Usa nomi DNS per cose, ad es. storage-lb.servicename si risolve negli indirizzi di tutti i servizi di bilanciamento del carico di archiviazione. Se vuoi aggiungerne uno, modifica semplicemente il DNS, tutti i servizi inizieranno ad usarlo automaticamente.

  • Keep It Simple. Più sistemi dipenderai, più il tuo servizio ne soffrirà.

La progettazione di sistemi ad alta disponibilità (HA) è un'area di ricerca e sviluppo attiva. Se guardi ACM o IEEE, ci sono un sacco di articoli di ricerca sulle qualità del servizio (disponibilità, affidabilità, scalabilità, ecc.) E su come raggiungerli (accoppiamento libero, adattamento, ecc.). Se cerchi più applicazioni pratiche, dai un'occhiata a sistemi e middleware a tolleranza d'errore creati per consentire funzionalità di clustering, grid o cloud.

La replica e il bilanciamento del carico (a.k.a. proxy inverso) sono alcuni dei modelli più comuni di realizzazione dei sistemi HA, e spesso possono essere fatti senza apportare modifiche al codice al software sottostante supponendo che non sia troppo stretto. Anche molte delle recenti offerte cloud sono ottenute essenzialmente attraverso la replica e il bilanciamento del carico, sebbene tendano a costruire l'elasticità per gestire un'ampia gamma di richieste del sistema.

Rendere i componenti software senza stato semplifica l'onere della replica, poiché lo stato stesso non deve essere replicato insieme ai componenti software. L'apolidia è uno dei principali motivi per cui l'HTTP si ridimensiona così bene, ma spesso richiede che le applicazioni si aggiungano nel proprio stato (ad es. Sessioni) che devono quindi essere replicate.

Pertanto, è più facile rendere i sistemi ad accoppiamento libero altamente disponibili rispetto ai sistemi ad accoppiamento stretto. Poiché l'affidabilità dei componenti del sistema determina l'affidabilità complessiva del sistema, potrebbe essere necessario sostituire componenti non affidabili (guasti hardware, bug del software, ecc.). Se si consente l'adattamento dinamico in fase di esecuzione, è possibile sostituire questi componenti non funzionanti senza influire sulla disponibilità dell'intero sistema. L'accoppiamento lento è un altro motivo per l'uso di sistemi di messaggistica affidabili in cui mittente e destinatario non devono essere disponibili contemporaneamente, ma il sistema stesso è ancora disponibile.

A quanto ho capito, stai cercando schemi specifici da utilizzare nelle applicazioni Java parte di un'architettura HA. Naturalmente c'è un numero numeroso di modelli e migliori pratiche che possono essere utilizzati, ma questi non sono in realtà "modelli HA". Piuttosto, sono buone idee che possono essere utilizzate in contesti many.

Suppongo che ciò che sto cercando di dire sia questo: un'architettura ad alta disponibilità è composta da numerose piccole parti. Se prendiamo una di queste piccole parti e le esaminiamo, probabilmente scopriremo che non ci sono attributi HA magici per questo piccolo componente. Se esaminiamo tutti gli altri componenti, troveremo la stessa cosa. È quando vengono combinati in modo intelligente per diventare un'applicazione HA.

Un'applicazione HA è un'applicazione in cui si pianifica il peggio dall'inizio. Se pensi mai in termini di " Questo componente è così stabile che non abbiamo bisogno di ridondanza aggiuntiva per esso " probabilmente non è un'architettura HA. Dopotutto, è facile gestire gli scenari problematici previsti. È quello che ti sorprende che fa cadere il sistema.

Nonostante tutto, ci sono modelli che sono particolarmente utili nei contesti di HA. Molti di questi sono documentati nel libro classico " Patterns of Enterprise Application Architecture " di Martin Fowler.

Sto interpretando " Alta disponibilità " come " Zero tempi di inattività " `, che può essere implementato come da altra domanda SE:

Distribuzione zero dei tempi di inattività per le app Java

  1. Interruttore A / B: (aggiornamento a rotazione + meccanismo di fallback)
  2. Distribuzione parallela - Apache Tomcat: (solo per applicazioni Web)
  3. Rilegatura porta ritardata
  4. Rilegatura avanzata delle porte

Userò alcuni di questi concetti per elaborare modelli di progettazione per il sistema ad alta disponibilità dal punto di vista del software, che complimenti sopra gli approcci.

Pattern da usare:

Proxy / Factory :

Avere un oggetto proxy e il proxy deciderà dove reindirizzare le richieste. Supponi di avere la versione 1 & amp; Versione 2 del software. Se i client si connettono con il vecchio protocollo, reindirizzarli al software versione 1. I nuovi client possono connettersi direttamente alla versione 2. Il proxy può avere il metodo Factory o AbstractFactory per eseguire il rendering di una nuova versione del software.

Strategia

Puoi modificare l'algoritmo in fase di esecuzione selezionando un algoritmo da una famiglia di algoritmi. Se prendi l'esempio delle compagnie aeree, puoi passare dagli algoritmi DiscountFare a NormalFare durante i mesi di traffico non di punta e di picco.

Decorator :

È possibile modificare il comportamento dell'oggetto in fase di esecuzione. Aggiungi una nuova classe e decora la responsabilità aggiuntiva.

Adapter :

Utile quando cambi interfaccia o contratto tra la versione 1 e la versione 2. L'adattatore risponderà sia al vecchio & amp; richieste di nuovi clienti in modo appropriato.

Linee guida generali:

  1. Accoppiamento libero tra oggetti
  2. Segui i S.O.L.I.D nella tua applicazione

Fai riferimento agli articoli del sito Web sourcemaking per i modelli sopra riportati per una migliore comprensione.

Cosa non usare:

Oltre ai modelli di progettazione, è necessario prendere alcune precauzioni per ottenere zero tempi di inattività per l'applicazione.

  1. Non introdurre singoli punti di errore nel sistema.
  2. Usa le cache distribuite (es. terracotta) / le serrature con parsimonia.
  3. Rimuovere l'accoppiamento rigido tra i servizi. Rimuovere l'accoppiamento stretto tra i servizi utilizzando bus / framework di messaggistica (JMS, ActiveMQ ecc.)

L'alta disponibilità riguarda più la disponibilità e la ridondanza dell'hardware che le convenzioni di codifica. Ci sono un paio di schemi che userei in quasi tutti i casi di HA: sceglierei il modello singleton per il mio oggetto di database e userei il modello factory per creare il singleton. La fabbrica può quindi avere la logica per gestire i problemi di disponibilità con il database (che è dove si verificano la maggior parte dei problemi di disponibilità). Ad esempio, se il Master è inattivo, connettersi a un secondo Master sia per le letture che per le scritture fino a quando il Master non è tornato. Non so se questi sono i modelli più sfruttati, ma sono i più sfruttati nel mio codice.

Naturalmente questa logica potrebbe essere gestita in un metodo __construct, ma un modello di fabbrica ti permetterà di controllare meglio il tuo codice e la logica decisionale su come gestire i problemi di connettività del database. Una factory ti permetterà anche di gestire meglio il modello singleton.

Eviterei assolutamente il motivo decorativo e il motivo osservatore . Entrambi creano complessità nel codice che ne rende difficile la manutenzione. Sono i casi in cui sono la scelta migliore per le tue esigenze, ma il più delle volte non lo sono.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top