Parte IV — Sistemi distribuiti · Capitolo 17

Oggetti distribuiti e servizi

~14 min di lettura2 widget interattivi

In questo capitolo

  1. Modelli di programmazione tradizionali
  2. Limiti dei paradigmi tradizionali
  3. Verso nuovi paradigmi distribuiti
  4. Quadro del modulo: Java RMI e Assignment 4
  5. Service-Oriented Computing

1. Modelli di programmazione tradizionali

Il modello di programmazione canonico per i sistemi distribuiti è semplice: applicazioni come insiemi di processi heavyweight che comunicano scambiandosi messaggi attraverso canali. Ogni processo può avere molteplici thread che comunicano attraverso memoria condivisa internamente. Su questo modello base si sono innestati diversi paradigmi tradizionali.

L'RPC estende il paradigma procedurale classico ai sistemi distribuiti. L'idea è che un programma possa chiamare una procedura eseguita su un nodo remoto come se fosse locale. La chiamata viene impacchettata: i parametri vengono serializzati (marshaling), inviati su TCP o UDP, ricevuti dall'altra parte, despacchettati (unmarshaling), e la procedura viene eseguita. Il componente che fa il marshaling si chiama proxy; quello che riceve si chiama stub. A livello applicativo, tutto è trasparente. Storicamente, questa è stata l'architettura dominante dagli anni '80 in poi, con implementazioni come Sun RPC e DCE/RPC.

Con l'avvento della programmazione orientata agli oggetti, l'RPC è stato esteso al Distributed Object Computing. L'idea è che oggetti su nodi diversi possano invocare metodi gli uni degli altri come se fossero locali. Java RMI (Remote Method Invocation) e CORBA (Common Object Request Broker Architecture) sono gli esempi più noti. Il professore sottolinea come Java, quando uscì nel 1996, avesse già abbracciato completamente questa idea, mentre Microsoft proponeva DCOM (Distributed Component Object Model). L'astrazione è potente: un oggetto Counter con interfaccia remota può essere invocato da un altro nodo con una semplice chiamata a metodo, nascondendo la complessità della rete.

Il message passing è il modello più esplicito e meno astratto: i processi comunicano inviando e ricevendo messaggi attraverso canali. Non c'è trasparenza: il programmatore sa che sta comunicando attraverso la rete e deve gestire esplicitamente l'asincronia, la serializzazione, l'indirizzamento. Questo modello è alla base dei Message-Oriented Middleware (MOM), del paradigma ad attori (Hewitt, 1973), e di sistemi moderni come Apache Kafka, RabbitMQ e ZeroMQ. È il modello che, come vedremo, meglio cattura la realtà dei sistemi distribuiti.

Nota del redattore

Il professore sottolinea che il middleware RMI può essere utile come componente per costruire altri middleware, ma non è un modello generale per programmare sistemi distribuiti. Il salto di qualità avviene quando si abbandona l'illusione della trasparenza.

2. Limiti dei paradigmi tradizionali

Nonostante l'apparente eleganza, l'idea di applicare i paradigmi tradizionali (procedurale, OOP) alla computazione distribuita presenta un problema fondamentale: la trasparenza non funziona. Questo è il punto centrale di un articolo fondamentale del 1994: «A Note on Distributed Computing» di Jim Waldo, Geoff Wyant, Ann Wollrath e Sam Kendall.

Il problema è che rendere trasparente la distribuzione — far sembrare una chiamata remota identica a una chiamata locale — nasconde differenze cruciali. Una chiamata locale è veloce, affidabile, passa attraverso memoria condivisa. Una chiamata remota è lenta, può fallire per guasti di rete o del nodo remoto, richiede serializzazione dei dati, ha una latenza di diversi ordini di grandezza superiore. Pensare che siano uguali porta a problemi ingegneristici seri: l'assenza di un criterio di località può generare catene di chiamate remote che si intrecciano, causando deadlock e race condition. Il controllo sulla distribuzione viene perso.

Insidia ingegneristica

Immaginate di fare load balancing in un sistema dove tutte le chiamate sono trattate come locali. Un oggetto chiama un oggetto remoto, che ne chiama un altro, poi un altro ancora, in una catena che potrebbe persino tornare indietro. Il risultato? Deadlock immediati, corsa critica, perdita totale del controllo sulla località delle computazioni.

La conclusione è netta: la trasparenza è un'illusione pericolosa. I sistemi distribuiti richiedono di gestire esplicitamente aspetti che non possono essere astratti: latenza, guasti parziali, concorrenza, eterogeneità. I modelli che funzionano abbracciano queste differenze invece di nasconderle. Si passa quindi a modelli basati su scambio di messaggi e architetture service-oriented, dove le interazioni sono esplicitamente asincrone e la località è un aspetto centrale del progetto.

3. Verso nuovi paradigmi distribuiti

Superati i limiti dei paradigmi tradizionali, la ricerca e l'ingegneria si sono orientate verso approcci basati sulla decentralizzazione del controllo e su modelli di interazione asincrona. Le principali direzioni emerse sono molteplici.

Le architetture Service-Oriented (SOA) e, in particolare, i microservizi scompongono il sistema in servizi indipendenti, ciascuno con un proprio dominio di responsabilità, che comunicano attraverso protocolli ben definiti come REST, gRPC o messaggistica asincrona. Il Domain-Driven Design fornisce gli strumenti concettuali per delimitare i confini dei servizi (bounded context).

Le architetture Event-Driven portano il disaccoppiamento ancora oltre: i componenti comunicano attraverso eventi asincroni. Un evento rappresenta un fatto accaduto nel sistema; chi lo produce non conosce né deve conoscere i consumatori. Questo disaccoppiamento temporale e spaziale è alla base di sistemi altamente scalabili e resilienti, come le architetture stream-based (Apache Kafka) e i microservizi event-driven.

Il paradigma ad Attori (Actor Model), proposto da Carl Hewitt nel 1973, anticipava già queste idee. Ogni attore è un'entità autonoma con un proprio stato, che comunica solo attraverso messaggi asincroni. Non c'è memoria condivisa, non c'è chiamata sincrona bloccante. Questo paradigma è stato ripreso modernamente da Akka, Erlang/OTP e Orleans, ed è considerato da molti il modello più naturale per il calcolo distribuito.

Il cloud computing completa il quadro fornendo piattaforme (AWS, Azure, GCP) che offrono infrastruttura, piattaforma e software come servizio, permettendo di costruire sistemi distribuiti su larga scala con astrazioni sempre più alte e scalabilità orizzontale demandata all'infrastruttura.

Idea chiave

Il filo conduttore è il passaggio dalla sincronia all'asincronia, dall'accentramento alla decentralizzazione, dalla trasparenza all'esplicitazione della distribuzione. I sistemi distribuiti di successo non nascondono la complessità: la gestiscono.

4. Quadro del modulo: Java RMI e Assignment 4

La lezione del 18 maggio conclude il modulo sugli algoritmi distribuiti (module 4.2) e introduce l'architettura Service-Oriented (module 4.3). Il professore dedica la prima parte all'Assignment 4, che mette in pratica due paradigmi fondamentali del distributed computing: il message passing con il modello degli attori, e il distributed object computing con Java RMI.

Idea chiave

L'Assignment 4 chiede di scegliere consapevolmente tra due filosofie: attori (messaggi asincroni, isolamento, nessuna memoria condivisa) versus RMI (chiamate a metodo sincrone, trasparenza oggettuale, middleware per la distribuzione). Non esiste una scelta giusta in assoluto: dipende dal problema da risolvere.

Assignment 4 — i due esercizi obbligatori

Il primo esercizio riguarda gli attori come modello di riferimento per il message passing, utilizzando un middleware a scelta (Akka, Quasar o altri). Il secondo esercizio applica la visione del distributed object computing con Java RMI. Il professore propone come esempio un sistema distribuito per giocare a Tic-Tac-Toe (tris) dove due giocatori su nodi diversi interagiscono attraverso oggetti remoti. La consegna e volutamente con pochi requisiti: lo studente deve fare le scelte progettuali in autonomia.

Nota del redattore

Il framework di riferimento usato nel corso per il modello ad attori è Apache Pekko, il fork open-source di Akka. Per la pratica guidata su RMI e MOM in vista dell'Assignment 4, vedi la pagina Prep esame — Assignment 4: Alarm distribuito + RMI TTT.

Per l'esame

Il professore sottolinea che non bisogna affidarsi ciecamente all'AI per questi esercizi: "se vi fate aiutare dalle AI, fatemi aiutare bene, perche non e detto che vi aiuti bene". I principi del corso (incapsulamento del flusso di controllo, componenti attivi/passivi, modularita) sono concetti che un assistente AI non padroneggia automaticamente. E molto meglio pensare e programmare toccando con mano.

Java RMI come esempio di middleware per distributed object computing

Java RMI (Remote Method Invocation) e un middleware che implementa il paradigma del distributed object computing. Consente di invocare metodi su oggetti che risiedono in JVM diverse, potenzialmente su macchine differenti. L'architettura si basa su quattro pilastri:

Il professore spiega che la RemoteException puo manifestarsi in scenari diversi: il nodo remoto non e raggiungibile al momento della chiamata, oppure la connessione cade durante l'esecuzione del metodo, prima che il risultato torni indietro. In tutti i casi, e lo stub (proxy) a gestire la situazione e sollevare l'eccezione appropriata.

Nota del redattore

Il professore accenna anche a RabbitMQ come esempio di message-oriented middleware (MOM), visitandolo brevemente nell'ottica dell'Assignment 4. La differenza fondamentale: RMI e sincrono e orientato agli oggetti, RabbitMQ e asincrono e orientato ai messaggi. Uno e basato su distributed object computing, l'altro su message-oriented middleware.

5. Service-Oriented Computing

Con il modulo 4.3, il professore introduce una prospettiva architetturale e ingegneristica del software distribuito. Mentre gli algoritmi distribuiti risolvono problemi specifici (consensus, mutua esclusione, ordinamento), il Service-Oriented Computing e un paradigma architetturale che abbraccia l'intero ciclo di vita del software distribuito.

Servizi e microservizi

Un servizio e un componente software autonomo, indipendente, con un confine ben definito e un contratto esplicito (tipicamente un'interfaccia). I microservizi estendono questo concetto portandolo all'estremo: servizi piccoli, focalizzati su un singolo dominio di business, deployabili indipendentemente, che comunicano via rete (tipicamente HTTP/REST o messaggi asincroni).

Idea chiave

Il passaggio dal distributed object computing (Java RMI, CORBA) al service-oriented computing non e solo tecnico: e un cambio di paradigma. Invece di nascondere la distribuzione dietro oggetti remoti (trasparenza), i servizi abbracciano esplicitamente la loro natura distribuita, con contratti, interfacce e API ben definite.

Domain-Driven Design (DDD)

Il Domain-Driven Design e strettamente correlato ai microservizi. Il concetto chiave e il bounded context: ogni microservizio dovrebbe mappare esattamente un bounded context del dominio applicativo. Questo garantisce che:

Architetture event-driven e stream-based

Oltre ai microservizi tradizionali (sincroni, REST), il professore menziona due architetture emergenti: