Architettura interna idiomatica dei microservizi Go

4

Ai fini dell'apprendimento, sto cercando di implementare un piccolo progetto usando i microservizi ( avviso buzzword! ). Ci sono molte risorse online che parlano del mondo dei microservizi "macro" - integrazione, orchestrazione, scoperta dei servizi, controllo della salute, topologia, ecc., Tuttavia ho scoperto che ci sono poche informazioni sulle implementazioni interne.

Il migliore che ho trovato finora è questo diagramma dal sito di Martin Fowler. Descrive una possibile configurazione interna utilizzando un modello di facciata con un servizio che incapsula un dominio e un repository.

Tuttavia, dopo aver avuto difficoltà a provare a implementare questi pattern da solo, e dopo aver esaminato varie altre opinioni , a quanto pare questi tipi di astrazioni non si adattano bene alla filosofia generale di Go di adottare l'approccio più diretto?

Il mio pensiero iniziale è che la maggior parte delle volte non c'è semplicemente bisogno di questo tipo di astrazione data la natura dei microservizi, assumendo che siano stati opportunamente partizionati in specifici contesti limitati e limitati; se un servizio è di poche centinaia di punti, allora è meglio riscrivere una palla di fango strettamente accoppiata, quindi progettare le astrazioni perfette solo per motivi di test / riusabilità.

In generale, è questo il caso? Ci sono altri schemi che potrebbero meglio adattarsi al paradigma Go per progettare gli interni di un microservizio? Non sono necessariamente "indottrinato" in alcun dogma particolare, ma sono interessato a leggere i vari punti di vista.

    
posta Anchor 26.05.2016 - 04:49
fonte

1 risposta

3

I microservizi esterni sono esattamente questo: esterno. Dovresti considerarli come una dipendenza con cui interagisci tramite HTTP e quali non devono essere considerati attendibili.

  • Il fatto che non ci si debba fidare crea una prima serie di difficoltà. Anche se hai un'interfaccia ben specificata, non dovresti assumere che l'altra parte obbedirà ad essa. Potrebbe iniziare a rispondere a cose strane, ma più probabilmente potrebbe portare un comportamento specifico come il blocco delle richieste quando vengono eseguite troppo spesso.

  • Il fatto che HTTP venga utilizzato crea ulteriore complessità. Cosa succede se il server DNS non funziona? Cosa succede se la connessione di rete è caduta nel mezzo del trasferimento?

Questo ti costringe a pensare a gestire casi eccezionali che di solito non gestisci all'interno del processo stesso:

  • Gestione e segnalazione di errori relativi alla dipendenza,
  • Failover, se pertinente,
  • Gestione dei timeout,
  • Continuità del servizio se la dipendenza non risponde più,
  • ecc.

(Un buon libro su questi aspetti è Release It! di Michael T. Nygard.)

Questo significa che dovresti prendere in considerazione un'architettura che tenga in considerazione l'inaffidabilità della dipendenza. In sostanza, questo significa che:

  • Il livello che si occupa del microservizio dovrebbe rendere sicuro per la tua applicazione l'utilizzo del microservizio. Dovrebbe gestire i timeout, i problemi con la rete e le modifiche nell'interfaccia nel miglior modo possibile. Sfortunatamente, finirà per essere un'astrazione che perde, ma è ancora più facile gestire i problemi relativi ai servizi a questo livello piuttosto che lasciare che le eccezioni scorrano liberamente all'interno dei livelli principali dell'applicazione.

    Allo stesso modo, il livello che gestisce il database dovrebbe, idealmente, comprendere tutta la logica relativa al database. Se improvvisamente hai bisogno di sostituire Oracle con PostgreSQL, l'impatto sul tuo codice dovrebbe essere strettamente limitato a questo livello.

  • Ogni client di microservizio dovrebbe essere isolato dagli altri microservizi. Non è una grande classe che gestisce le chiamate a venti microservizi, ma piuttosto venti parti indipendenti che gestiscono un microservizio ciascuna; possono ancora condividere (ereditare, qualunque cosa) lo stesso codice quando necessario per evitare la duplicazione del codice.

    Allo stesso modo, se la tua applicazione si basa su MongoDB e Redis, non metti una sola grande classe che li gestisce entrambi. Avete fornitori specifici che gestiscono MongoDB e altri fornitori che si occupano di Redis. Sostituire Redis con qualche altro archivio di strutture dati significa che il codice relativo a MongoDB rimarrà invariato.

  • L'app potrebbe dover gestire il caso in cui il microservizio non è più disponibile. Ad esempio, se stai creando un sito di e-commerce, il fatto che il microservizio che gestisce i commenti degli utenti dei prodotti non risponda non dovrebbe impedirti di mostrare la home page del sito o continuare a vendere prodotti.

    Allo stesso modo, la perdita di LDAP potrebbe essere un problema per le parti di un sito di e-commerce che in realtà richiedono l'autenticazione, ma l'applicazione può ancora mostrare la sua home page agli ospiti. Se perdere LDAP è uno scenario probabile (spero non sia mai così), cambiare il sito di e-commerce in modo che gli acquisti possano ancora essere fatti senza LDAP potrebbe essere una soluzione.

  • Sostituire un microservizio con la sua versione più recente dovrebbe interessare solo una piccola parte della tua applicazione e non dovrebbe propagarsi a ogni livello. Le nuove versioni dei microservizi saranno rilasciate, e quelle più vecchie saranno diventate obsolete, quindi la tua vita sarà molto più facile se si progetta il codice dall'inizio per poterlo scambiare microservice client con un altro facilmente.

    Allo stesso modo, le modifiche a Twilio, Facebook o all'API di Google Maps non dovrebbero causare troppi problemi. Potresti dover aggiustare alcune classi che fanno esattamente questo, trattare quelle API e presentarle alla tua app come un'astrazione, ma le modifiche non dovrebbero propagarsi attraverso i livelli dell'applicazione.

risposta data 26.05.2016 - 09:55
fonte

Leggi altre domande sui tag