Sebbene molte applicazioni siano sviluppate in modo modulare, spesso esse vengono distribuite come un unico artefatto (un JAR, un WAR, un EAR, etc.), che contiene al suo interno la logica di business, di presentazione, la logica di accesso allo storage, di integrazione con servizi esterni, etc. Questo pattern architetturale, comunemente denominato “architettura esagonale”, genera “monoliti” che tendono a crescere nel tempo e a diventare enormi. Durante ogni ciclo di sviluppo, infatti, vengono implementi nuovi casi d’uso, con la conseguente aggiunta di diverse righe di codice al sorgente originale. Dopo breve tempo, una piccola e semplice applicazione potrebbe essere cresciuta al punto da trasformarsi in un artefatto poco gestibile, difficile da migliorare, da scalare, da correggere e da comprendere.
Le caratteristiche di complessità dei progetti “moderni” si prestano bene all’utilizzo di una architettura fondata sui microservizi. Il proposito di tale approccio è quello di creare un sistema costituito da moduli di piccole dimensioni, indipendenti gli uni dagli altri e in grado di comunicare tra di loro in modo snello. Un microservizio, infatti, implementa un sottoinsieme di funzionalità (un servizio di autenticazione, un servizio di gestione degli utenti, un servizio di pagamento, etc.) e si configura come un’applicazione a tutti gli effetti, che vive all’interno di un processo dedicato e che implementa una logica di business ben circoscritta. I vantaggi principali di questa tipologia di architettura sono molteplici:
- Flessibilità tecnologica: è possibile decidere di utilizzare tecnologie differenti all’interno di ciascuno microservizio. Questo approccio consente di scegliere lo strumento giusto per ogni tipo di esigenza, piuttosto che optare per un criterio condiviso da tutti i servizi che compongono il sistema.
- Resilienza: la separazione dei componenti di un’applicazione rende molto meno probabile che un bug o un problema si rifletta sull’intero sistema. Eventuali servizi “difettosi” possono essere isolati singolarmente, riparati e rimessi in funzione senza necessariamente interrompere le funzionalità dell’intera applicazione.
- Scalabilità: diversamente da un servizio monolitico, dove se una piccola parte del sistema necessita di prestazioni hardware particolari è necessario scalare tutte le parti dell’intero sistema, in una architettura a microservizi è possibile scalare i singoli servizi che, per loro natura, necessitano di risorse hardware ulteriori. Ciò consente di assegnare a ciascuna componente risorse hardware sufficienti a soddisfare le esigenze computazionali dello specifico servizio.
- Facilità di deployment: in una architettura a microservizi è possibile apportare una modifica ad un singolo servizio e effettuare il deployment dello stesso in modo indipendente dalle altre componenti.
Modello Logico
Nel disegno dell’architettura logica di una applicazione basata su microservizi è necessario considerare che esistono un insieme di componenti che devono essere inclusi obbligatoriamente per garantire il corretto funzionamento della piattaforma. La figura seguente mostra gli elementi architetturali che caratterizzano una architettura a microservizi, con particolare riferimento alla piattaforma Spring Cloud. Tali componenti sono descritte brevemente nei paragrafi seguenti.
Api Gateway
Scopo di tale componente è quello di gestire le richieste provenienti dai vari client e di fare da router verso i microservizi. In altri termini espone una interfaccia unica verso i client e si preoccupa di realizzare la logica associata alla richiesta in maniera trasparente ad esso. Un client, ad esempio, può chiamare un unico servizio (via internet), ma l’API gateway lo può realizzare invocando n° servizi distinti (in rete LAN ad alta velocità) per comporre poi l’output da inviare al chiamante. Questo non solo ottimizza la comunicazione tra il client e l’applicazione limitando di fatto il numero di servizi chiamati direttamente dal client (via internet), ma eventuali modifiche alla implementazione di un servizio o l’aggiunta di uno nuovo può avvenire in maniera trasparente. Zuul è l’API Gateway incluso nel framework Spring Cloud.
Service Registry
Tale componente si occupa della service discovery, ovvero di mantenere un registro di tutti i servizi disponibili in modo che i diversi moduli possano registrarvisi e trovarsi a vicenda. Per la natura delicata del compito che svolge è importante che il Service Registry sia sempre aggiornato ed altamente affidabile. Di conseguenza, il servizio deve essere realizzato da un cluster di server che utilizzano un protocollo di replica per mantenere la coerenza delle informazioni. Eureka è il Service Registry incluso nel framework Spring Cloud.
Config Server
I diversi moduli e servizi che implementano l’applicativo hanno configurazioni che sono naturalmente anch’esse distribuite. Tale componente fornisce servizi di configuration management, ovvero si occupa di centralizzare i file di configurazione e di ridistribuirli a caldo. Tali file possono essere conservati all’interno di repository distribuiti che ne mantengono il versionamento, come Git, oppure in semplici database o, in ultima analisi, su file system. Spring Cloud Config è il Configuration Manager incluso nel framework Spring Cloud.
Circuit Breaker
Un sistema distribuito deve essere tollerante al fallimento di una delle sue componenti. Se uno o più moduli risultano non disponibili, il sistema deve essere in grado di isolare quella componente e dare una risposta di default. Tale sistema si occupa dell’implementazione di tale logica aprendo, appunto, il circuito. Contestualmente deve controllando periodicamente la disponibilità del sistema escluso al fine, eventualmente, di richiudere il flusso delle chiamate verso lo stesso. Hystrix è il Circuit Breaker incluso nel framework Spring Cloud.
Client Side Load Balancer
Tale componente si integra con il Service Registry per il recupero di tutti i riferimenti ai servizi disponibili e rende trasparente la intercomunicazione tra le componenti applicative selezionando, ad esempio con modalità round-robin, quale replica del servizio invocare. Ribbon è il Load Balancer incluso nel framework Spring Cloud.
Distributed Tracing
In un ambiente distribuito ogni componente ha i propri log che devono essere recuperati ed aggregare in un unico sistema per riuscire a capire cosa sta avvenendo. Sleuth è la soluzione di Distributed Tracing inclusa nel framework Spring Cloud.