Parte IV — Sistemi distribuiti · Capitolo 19

Oggetti distribuiti, servizi e middleware

~25 min di letturaesempi Java RMI e RabbitMQ

In questo capitolo

  1. Middleware e trasparenza
  2. Perché la trasparenza non basta
  3. Java RMI
  4. Middleware orientati ai messaggi
  5. Servizi e architetture orientate ai servizi
  6. Web service: SOAP, WSDL e REST
  7. Microservizi e stili architetturali
  8. Cloud: dall'infrastruttura alle funzioni
  9. Verifica le tue conoscenze

Il capitolo in un paragrafo. Per programmare sistemi distribuiti si è prima cercato di nascondere la rete: chiamate di procedura remote, oggetti distribuiti, Java RMI. Ma guasti parziali e latenza non si nascondono, e l'idea di trasparenza è caduta. Sono rimasti i middleware, in particolare quelli a messaggi (MOM), che disaccoppiano i processi con code e publish/subscribe, e i servizi: componenti autonomi raggiungibili in rete tramite un contratto. Dai web service SOAP e REST si arriva ai microservizi, e il cloud offre infrastruttura, piattaforme, software e funzioni «as a service», pagando per l'uso invece di comprare in anticipo.

1. Middleware e trasparenza

Un middleware è uno strato software tra il sistema operativo e le applicazioni distribuite. Gira sui nodi con una vita propria, indipendente dalle applicazioni che lo usano, e offre loro servizi come la comunicazione, i nomi per trovare le risorse, la persistenza, così che le parti di un'applicazione interagiscano senza scendere al livello di socket e protocolli. Può essere a sua volta distribuito su un cluster, installato in casa o usato come servizio nel cloud.

Il modello canonico del capitolo 17 sono processi che si scambiano messaggi. La tentazione storica è stata continuare a programmare con il paradigma noto e lasciare al middleware il compito di rendere trasparente la distribuzione.

La comunicazione può essere transitoria, solo tra processi in esecuzione nello stesso momento (socket, RPC, RMI e CORBA, MPI), oppure persistente: i messaggi restano nel middleware finché il destinatario non li preleva, come nelle code dei MOM (sezione 4).

Write once, run anywhere?

Lo slogan di Java promette una trasparenza simile rispetto alla piattaforma: il bytecode gira uguale su ogni JVM. Regge per il codice sequenziale, non con i thread. I thread Java poggiano su quelli del sistema operativo, quindi scheduling, priorità, numero massimo di thread e memoria dipendono dal sistema: un programma che crea molti thread può fermarsi su un sistema operativo e non su un altro. E l'interleaving cambia da macchina a macchina, per cui una race condition mai vista su un computer può comparire su un altro con più core o uno scheduler diverso. La correttezza di un programma concorrente non può dipendere dalla piattaforma su cui è stato provato (capitolo 10).

2. Perché la trasparenza non basta

L'articolo A Note on Distributed Computing (Waldo et al., 1994) spiega perché applicare l'OOP al distribuito astraendo dalla distribuzione non funziona. Gli argomenti sono due.

I guasti parziali. Se durante una chiamata c.inc() la rete cade, il chiamante deve saperlo. Ma per il dominio inc() non ha motivo di sollevare eccezioni: bisogna aggiungerne una estranea al dominio (in Java RemoteException) e il codice per gestirla. Lo stesso codice non funziona quindi identico in locale e in remoto: il caso locale è un caso particolare del distribuito, non il contrario. Con gli attori il problema non si pone, perché send non ha mai promesso che il messaggio arrivi: se serve una conferma, fa parte del protocollo.

La latenza. Una chiamata locale costa un microsecondo o meno, una remota da un millisecondo in LAN a centinaia nel cloud: almeno tre ordini di grandezza in più. Trattare allo stesso modo le interazioni tra oggetti vicini e tra oggetti su nodi diversi ignora il principio di località. E con chiamate tutte sincrone, catene di chiamate che attraversano i nodi e tornano indietro portano facilmente a deadlock.

Le Lab Notes sui MOM riassumono i problemi dell'RPC tradizionale in quattro punti: accoppiamento stretto (ogni cambio di interfaccia va propagato a tutti), affidabilità (il modello sincrono reagisce male ai guasti), scalabilità (le chiamate bloccanti pesano sulle prestazioni), disponibilità (entrambi i partecipanti devono essere attivi). La direzione è quindi il controllo decentralizzato e l'interazione asincrona: message passing, attori, middleware a messaggi. Hewitt pensò gli attori (1973) proprio per il distribuito; Ricci li paragona all'assembly del distribuito, una base su cui costruire astrazioni più alte. RMI resta utile come mattone per altri middleware, non come modello generale.

3. Java RMI

Java RMI (Remote Method Invocation) permette a un thread di una JVM di invocare un metodo su un oggetto che vive in un'altra JVM, anche su un altro nodo, con la sintassi di una chiamata locale. È il middleware del secondo esercizio dell'assignment 4, il tris distribuito, discusso nella pagina di preparazione.

// Interfacce remote: estendono Remote, ogni metodo dichiara RemoteException
public interface Counter extends Remote {
  void inc() throws RemoteException;
  int getValue() throws RemoteException;
  void addListener(CounterListener l) throws RemoteException;
}
public interface CounterListener extends Remote {
  void countIncremented(int value) throws RemoteException;
}

// Implementazione: una classe normale. Il runtime RMI la chiama da più thread.
public class CounterImpl implements Counter {
  private int value;
  private final List<CounterListener> listeners = new ArrayList<>();

  public void inc() {
    int v; List<CounterListener> ls;
    synchronized (this) { v = ++value; ls = List.copyOf(listeners); }
    for (var l : ls) {                      // callback remote, fuori dal lock
      try { l.countIncremented(v); } catch (RemoteException e) { /* client irraggiungibile */ }
    }
  }
  public synchronized int getValue() { return value; }
  public synchronized void addListener(CounterListener l) { listeners.add(l); }
}

// Server: esporta l'oggetto (ottiene lo stub) e lo registra con un nome
var stub = (Counter) UnicastRemoteObject.exportObject(new CounterImpl(), 0);
LocateRegistry.getRegistry().rebind("countObj", stub);

// Client: cerca per nome, poi usa l'oggetto come se fosse locale
class MyListener implements CounterListener {
  public void countIncremented(int v) { System.out.println("nuovo valore: " + v); }
}
var c = (Counter) LocateRegistry.getRegistry(host).lookup("countObj");
var me = (CounterListener) UnicastRemoteObject.exportObject(new MyListener(), 0);
c.addListener(me);     // passa uno stub: il server potrà richiamare il client
c.inc();               // sincrona: il thread aspetta la risposta dalla rete

4. Middleware orientati ai messaggi

Un MOM (Message-Oriented Middleware) è qualunque infrastruttura middleware che offre messaggistica: i client inviano e ricevono messaggi da altri client, con garanzie di affidabilità, sicurezza e prestazioni. È un pilastro dei sistemi enterprise e può essere centralizzato o distribuito. Rispetto all'RPC rende i messaggi espliciti, l'interazione asincrona e la comunicazione persistente: il middleware sta in mezzo e disaccoppia i partecipanti.

Dalle slide: concetti di base, interfaccia delle code e combinazioni di persistenza e sincronia

Concetti di base: canali (il mezzo di comunicazione), messaggi (i dati scambiati), pipes and filters (stadi di elaborazione dei messaggi), routing (composizione dei canali), trasformazione (conversione tra formati di dati eterogenei), endpoint (il collegamento di un'applicazione a un canale).

Interfaccia di una coda persistente: put aggiunge un messaggio alla coda; get si blocca finché la coda è vuota, poi rimuove il primo messaggio; poll controlla la coda e rimuove il primo messaggio senza bloccarsi mai; notify installa un gestore chiamato quando arriva un messaggio.

Persistenza e sincronia: (a) persistente asincrona; (b) persistente sincrona; (c) transitoria asincrona; (d) transitoria sincrona basata sulla ricevuta (il mittente riprende quando il messaggio è accettato dal sistema); (e) transitoria sincrona basata sulla consegna (riprende quando il messaggio è consegnato al destinatario); (f) transitoria sincrona basata sulla risposta (riprende quando arriva la risposta, come in RPC).

BeneficiSvantaggi
Affidabilità, grazie alla persistenza dei messaggi nei nodi del MOMinterazioni asincrone più complesse da gestire
Scalabilità: i sottosistemi si scalano indipendentemente, i picchi vengono assorbiti, il carico si bilanciala coordinazione passa tutta per i messaggi
Disponibilità: il sistema continua a funzionare anche se un partecipante è temporaneamente giùgarantire il corretto ordine degli eventi è responsabilità di chi usa il MOM

Code, broker e modelli di messaggistica

Il concetto fondamentale è la coda di messaggi, che conserva i messaggi sulla piattaforma. Le code si collegano tramite router oppure tramite broker, server che ricevono, conservano e smistano i messaggi. I modelli di messaggistica sono due:

Il consumatore riceve in pull, interrogando il provider, o in push, facendosi inviare i messaggi appena arrivano. Il filtraggio seleziona i messaggi ricevuti da un canale; i namespace gerarchici permettono di abbonarsi al livello giusto di un albero di sotto-canali, utile con grandi volumi di messaggi. Come qualità del servizio un MOM offre le garanzie di consegna già viste per gli attori: at-least-once, at-most-once, exactly-once (capitolo 15).

RabbitMQ

Il laboratorio usa RabbitMQ, un broker open source (si avvia comodamente con Docker). Un produttore pubblica su un exchange, che smista i messaggi nelle code: l'exchange di default consegna alla coda con il nome indicato (point-to-point), fanout copia il messaggio in tutte le code collegate (publish/subscribe), direct instrada in base a una chiave esatta, topic anche con pattern come kern.*.

var factory = new ConnectionFactory();
factory.setHost("localhost");

// Produttore: dichiara la coda e pubblica tramite l'exchange di default
try (var conn = factory.newConnection(); var ch = conn.createChannel()) {
  ch.queueDeclare("hello", false, false, false, null);
  ch.basicPublish("", "hello", null, "Hello World!".getBytes(StandardCharsets.UTF_8));
}

// Consumatore (un altro processo): la connessione resta aperta,
// la callback è eseguita da un thread del client RabbitMQ
var conn = factory.newConnection();
var ch = conn.createChannel();
ch.queueDeclare("hello", false, false, false, null);
DeliverCallback onMessage = (tag, delivery) ->
    System.out.println(new String(delivery.getBody(), StandardCharsets.UTF_8));
ch.basicConsume("hello", true, onMessage, tag -> {});   // autoAck = true

Con più consumatori sulla stessa coda i messaggi si distribuiscono tra loro (work queue); con autoAck = false il consumatore conferma a mano con basicAck dopo aver elaborato, così un messaggio non confermato torna in coda se il consumatore cade. L'esercizio opzionale dell'assignment 4 chiede di costruire su un MOM un piccolo middleware per sezioni critiche distribuite, con gli algoritmi del capitolo 18.

Dalle slide: standard, implementazioni ed event broker

Implementazioni: WebSphere MQ (ex MQSeries), TIBCO, SonicMQ, Herald, Hermes, SIENA, Gryphon, JEDI, REBECCA, OpenJMS; open source: Apache ActiveMQ (supporta AMQP, MQTT, OpenWire, REST, RSS e Atom, Stomp, WSIF, WS Notification, XMPP), RabbitMQ, MQTT per l'IoT.

Standard: AMQP (standard OASIS che definisce protocollo e formati, per implementazioni interoperabili), XMPP (protocollo per MOM basato su XML), JMS (Java Message Service), CORBA Event Service e Notification Service.

Dai messaggi agli eventi. Un evento è la registrazione di qualcosa di rilevante accaduto nel business (una fattura ricevuta, una sala prenotata, un'assunzione). Un message broker cancella i messaggi dopo la conferma di consumo; un event broker mantiene invece un log ordinato, immutabile e append-only di fatti, conservati quanto serve, da cui ogni consumatore può rileggere da qualunque punto: una fonte unica di verità. Un event broker può fare da message broker, non il contrario. Gli event broker sono il cuore delle piattaforme di event streaming, per l'analisi dei dati sia batch sia in tempo reale; l'esempio è Apache Kafka, publish/subscribe con topic gestiti da broker organizzati in cluster, alta scalabilità e bassa latenza.

5. Servizi e architetture orientate ai servizi

I servizi sono oggi l'approccio principale ai sistemi distribuiti su Internet. Un servizio è un componente software fornito attraverso un endpoint raggiungibile in rete: un provider lo offre, dei consumer lo usano scambiando messaggi. Due visioni sono in concorrenza e in convergenza: la SOA dei «big» web service (lo stack WS-*), nata per l'enterprise, e i web service RESTful, che espongono risorse; a queste si aggiungono i microservizi.

Una SOA (Service-Oriented Architecture) separa le responsabilità scomponendo la logica di business in unità più piccole e distinte, che insieme formano una logica più grande, distribuita logicamente e fisicamente. I servizi sono autonomi, anche nei linguaggi e nelle tecnologie, e interoperano grazie a standard condivisi, interfacce e contratti tra chi offre e chi usa. La descrizione di un servizio serve a scoprirlo e funziona da contratto; la comunicazione avviene con messaggi, unità indipendenti.

La SOA è uno stile architetturale: definisce componenti, relazioni e vincoli su uso e interazioni. I componenti sono sei:

ComponenteChe cos'è
Serviziofornisce una funzione di business distinta, un blocco di logica a grana grossa, e implementa tutto ciò che promettono i contratti che espone
Contratto (API)l'insieme di tutti i messaggi supportati dal servizio
Endpointl'URI, l'indirizzo in cui il servizio si trova; a un endpoint si espone un contratto
Messaggiol'unità di comunicazione: una richiesta HTTP, un messaggio SOAP…
Policytermini e condizioni dinamici per usare il servizio, separati dalla logica e modificabili a run time: sicurezza (cifratura, autenticazione, autorizzazione), auditing, SLA
Service consumeril componente che interagisce con il servizio tramite messaggi: un'applicazione client o un altro servizio

Otto principi guidano il progetto. I servizi sono:

Il beneficio principale è eliminare l'integrazione punto a punto. Le aziende crescono per sistemi isolati, uno per reparto, e ogni volta che due devono condividere dati nasce un collegamento ad hoc (database, file, ETL, chiamate dirette), fino a uno «spaghetti di integrazione» ingestibile. Con i servizi migliorano l'adattabilità (l'interno si cambia rispettando i contratti), la manutenibilità (team piccoli e dedicati) e la riusabilità, intesa come non buttare via tutto quando serve altro, non come «write once, integrate anywhere». I confini espliciti sostituiscono anche la «zuppa di oggetti». Per il business ne derivano sostituzione più facile dei componenti, time to market più breve, integrazione dei partner, meno downtime, SLA configurabili.

6. Web service: SOAP, WSDL e REST

I «big» web service

Un web service è una SOA con scelte tecnologiche precise: XML, SOAP e WSDL, più gli standard WS-* per sicurezza, affidabilità, trust e altre qualità del servizio. Obiettivo: interoperabilità tra middleware eterogenei e disaccoppiamento tra consumer e provider, che si scambiano documenti autocontenuti con pochissime ipotesi su chi li riceve. A differenza di RMI e CORBA non ci sono riferimenti a oggetti remoti, quindi nessun broker che gestisca uno spazio di indirizzi distribuito; si supportano sia lo stile RPC sia quello a messaggi.

<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
  <soap:Body>
    <m:GetPrice xmlns:m="http://www.w3schools.com/prices">
      <m:Item>Apples</m:Item>
    </m:GetPrice>
  </soap:Body>
</soap:Envelope>

Implementazioni: .NET WCF, la specifica JAX-WS (per esempio Apache Axis2), Eclipse Web Tools Platform.

REST

REST (REpresentational State Transfer) è lo stile architetturale per sistemi ipermediali su larga scala introdotto da Roy Fielding nella tesi di dottorato: spiega la scalabilità di HTTP/1.0 e ha guidato il progetto di HTTP/1.1. Componenti debolmente accoppiati, indipendenti da tecnologie e piattaforme, comunicano tramite interfacce su protocolli web standard, con la semplicità come principio. Unendo architettura software e architettura di rete (banda, throughput, latenza) è adatto ai sistemi in cui conta la scalabilità, e i web service RESTful sono oggi lo standard di fatto delle API web.

In una SOA REST il contratto di un servizio si scompone in capability, una per funzione offerta, e tutti i servizi condividono un contratto uniforme di tre elementi:

Tipo di servizioConfine funzionaleEsempio di capability
Entityuna o più entità di business (fattura, cliente…)GET /invoice/{id}, PUT /invoice/{id}/date
Taskun compito di business; il nome contiene un verbo, poche capability per controllarloPOST /start/{timesheet} di ValidateTimesheet
Utilitycapability correlate anche molto ampie, per esempio l'involucro di un sistema legacyGET /employee/{id} di HRSystem

Basta qualunque libreria HTTP, ma esistono API dedicate come JAX-RS (implementata per esempio da Jersey e RESTEasy; Spring e Vert.x, citati nelle slide, offrono API REST proprie). Le interfacce si specificano con OpenAPI, nato da Swagger, che genera anche stub client e skeleton server; alternative sono RAML e Restlet. GraphQL lascia al client descrivere query flessibili, così le API evolvono più facilmente. Le API web sono ormai attese da ogni applicazione, anche perché permettono ai fornitori di sapere chi usa i servizi e di farsi pagare a chiamata.

7. Microservizi e stili architetturali

I microservizi sono uno stile architetturale in cui una singola applicazione è una suite di piccoli servizi, ognuno nel proprio processo, che comunicano con meccanismi leggeri (API REST o messaggistica leggera). Sono costruiti attorno alle capacità di business, si rilasciano indipendentemente con deploy automatizzato, hanno una gestione centralizzata minima e possono usare linguaggi e database diversi. Anche i big web service sono servizi, ma standardizzano l'interoperabilità tra sistemi enterprise con uno stack pesante (SOAP, WSDL, WS-*); i microservizi scompongono una applicazione in servizi piccoli, con protocolli leggeri, deploy e dati indipendenti.

Stili architetturali

L'architettura di un sistema è l'insieme delle strutture necessarie per ragionarci: elementi software, relazioni e proprietà di entrambi (Bass et al.); serve a dividere il lavoro tra team e a soddisfare i requisiti di qualità. Uno stile architetturale definisce una famiglia di sistemi con lo stesso schema: il vocabolario di componenti e connettori e i vincoli su come combinarli (Garlan e Shaw). Le slide ne confrontano quattro:

StileIdeaNota
A stratiogni strato ha responsabilità precise e dipende solo da quello sotto (o da quelli sotto); tipicamente presentazione, logica di business, persistenzadifetti: un solo strato di presentazione e uno di persistenza, mentre un'applicazione ha più client e più database; la logica dipende dalla persistenza e non si testa senza database
Esagonalela logica di business al centro, con porte (in Java, interfacce) in ingresso (le API che espone) e in uscita (come chiama l'esterno); adattatori in ingresso e in uscita collegano il mondo esternola logica non dipende dagli adattatori, sono loro a dipendere da lei
Monoliticola vista di implementazione è un solo componente, un unico eseguibile o WARla vista logica può comunque essere esagonale
A microservizila vista di implementazione è un insieme di componenti: i servizi; i connettori sono i protocolli di comunicazioneogni servizio ha la propria architettura logica, tipicamente esagonale

API e interazioni

L'API di un servizio espone comandi, query ed eventi. Tutta l'interazione passa da lì, e questo rende i servizi debolmente accoppiati: l'implementazione può cambiare senza toccare i client. Le interazioni si classificano su due assi:

uno a unouno a molti
sincronarequest/response: il client aspetta, magari bloccato; tende ad accoppiare strettamente i servizi—
asincronarequest/response asincrona (il client non si blocca); notifica one-way, senza rispostapublish/subscribe; publish/async responses (si pubblica una richiesta e si raccolgono le risposte per un certo tempo)

Le tecnologie di comunicazione sono REST, usato come trasporto per chiamate RPC (che accoppia) o in stile davvero RESTful (che disaccoppia), eventualmente con WebSocket per gli eventi; i messaggi su MOM, asincroni e disaccoppianti; gRPC e GraphQL tra le più recenti.

Come si organizzano

I servizi sono componenti fuori processo, a differenza delle librerie chiamate in memoria. Si rilasciano e scalano uno per uno, purché i contratti evolvano con giudizio, e hanno un confine di modulo netto, mentre in un monolite spesso solo la disciplina protegge l'incapsulamento. Il prezzo: le chiamate remote costano molto di più, quindi le API devono essere a grana più grossa.

La scomposizione è per capacità di business, non per tecnologia. Se i team si dividono per strato (interfaccia, logica, database), anche una modifica semplice diventa un progetto tra team; con i microservizi ogni servizio copre un'area dall'interfaccia ai dati, con un team cross-funzionale. Il riferimento è il Domain-Driven Design (Evans): un modello del dominio con il vocabolario del team (ubiquitous language), diviso in sottodomini; l'ambito di un modello è un bounded context, che diventa un servizio o un gruppo di servizi. L'approccio API-first scrive prima l'interfaccia con un IDL (OpenAPI, RAML), la rivede con i client e solo dopo implementa.

Pro dei microserviziContro e sfide
rilascio e scalabilità indipendenti per serviziorichiedono di progettare un sistema distribuito
team autonomi, organizzati per area di businesslatenza di rete su ogni interazione
libertà di linguaggi e tecnologie di memorizzazionecomunicazione tra processi complessa; la comunicazione sincrona riduce la disponibilità
confini espliciti, manutenibilità e testabilitàmantenere i dati consistenti tra servizi
un guasto resta confinato al servizioottenere una vista consistente dei dati

8. Cloud: dall'infrastruttura alle funzioni

Il cloud computing è una delle scelte principali per le applicazioni distribuite su larga scala, per scalabilità, apertura ed eterogeneità, disponibilità. È un cambio di paradigma nell'erogazione dei servizi informatici: servizi, calcolo e dati si spostano, per costo e convenienza, off-premise, in un impianto centralizzato e trasparente rispetto alla posizione, invece che on-premise. Lo hanno trainato Google, Yahoo, Amazon, che hanno insegnato al mondo a costruire architetture massivamente scalabili. I dati nel cloud sono più facili da raggiungere, costano meno e si prestano a collaborazione e analisi; restano da valutare prestazioni e sicurezza.

Da CapEx a OpEx

Prima, lanciare un servizio richiedeva una grande CapEx (spesa in conto capitale): comprare in anticipo l'infrastruttura per il picco di domanda. Il cloud fa seguire le spese all'uso effettivo delle risorse, e quindi agli incassi, in tre modi: trasforma la CapEx in OpEx (spesa operativa); elimina buona parte del personale che amministra i server; rende facile crescere in caso di successo, senza costose modifiche architetturali. Con costi di lancio bassi si provano più servizi senza rischiare investimenti in infrastruttura che potrebbe non servire.

I modelli di servizio

ModelloChe cosa offreEsempi delle slide
IaaS, Infrastructure as a Serviceinfrastruttura di calcolo e memorizzazione, centralizzata e trasparente rispetto alla posizioneAmazon EC2, Google Compute Engine, OpenStack
PaaS, Platform as a Servicegli elementi di base per sviluppare nuove applicazioniGoogle App Engine, Windows Azure
SaaS, Software as a Serviceapplicazioni complete accessibili in reteGoogle Apps, Salesforce, WebEx

Salendo da IaaS a SaaS il fornitore gestisce sempre più livelli e il cliente sempre meno. I principali fornitori sono Amazon AWS, Google Cloud Platform, Microsoft Azure, Heroku; Cloud Foundry è una PaaS open source. Le PaaS hanno in comune un'architettura a servizi (REST, GraphQL, gRPC) e un insieme di servizi di base: memorizzare e recuperare dati, eseguire e gestire attività di lunga durata, comunicare, configurare e gestire le applicazioni.

FaaS e serverless

Il FaaS (Function as a Service) porta nel cloud codice organizzato in funzioni, attivate da eventi definiti dallo sviluppatore. È simile a una PaaS ma più leggero: più reattivo e con scalabilità automatica. Esempi: AWS Lambda, Google Cloud Functions. Oggi si parla di serverless computing, che ha quattro caratteristiche:

È adatto a problemi scomponibili in parti senza stato, piccole e dal comportamento prevedibile. Le funzioni sono effimere, con una durata nota e limiti di tempo imposti dalla piattaforma, e non conservano stato tra un'invocazione e l'altra: se l'applicazione ha uno stato, sta in un data store.

Dalle slide: oltre questa panoramica

Verifica le tue conoscenze

Che cos'è un middleware? E un MOM?

Un middleware è uno strato software tra sistema operativo e applicazioni distribuite, con vita propria, che offre servizi come comunicazione e nomi. Un MOM è un middleware che offre messaggistica: code, publish/subscribe, persistenza e garanzie di consegna, disaccoppiando nel tempo e nello spazio chi invia da chi riceve.

Perché la trasparenza di RPC e oggetti distribuiti non funziona?

Perché chiamate locali e remote differiscono in modo sostanziale: le remote possono fallire parzialmente (serve RemoteException) e costano ordini di grandezza in più, quindi la località conta. Catene di chiamate sincrone tra nodi portano anche a deadlock.

In Java RMI i parametri passano per valore o per riferimento?

Gli oggetti normali per valore, serializzati: devono implementare Serializable, altrimenti l'errore arriva a run time. Gli oggetti remoti esportati per riferimento, perché viaggia il loro stub: così il server può richiamare il client.

Perché un oggetto remoto RMI va sincronizzato?

Perché il runtime RMI serve le chiamate con un pool di thread e più client possono invocare metodi dello stesso oggetto in parallelo: l'oggetto è stato condiviso e va protetto come un monitor.

Perché i thread fanno venir meno il principio «write once, run anywhere»?

I thread Java poggiano su quelli del sistema operativo: scheduling, limiti sul numero di thread e memoria dipendono dalla piattaforma, e l'interleaving cambia da una macchina all'altra. Un programma con una race condition può funzionare su un computer e fallire su un altro.

Point-to-point o publish/subscribe?

Point-to-point: produttori depositano in una coda e ogni messaggio va a un solo consumatore. Publish/subscribe: i messaggi pubblicati su un topic arrivano a tutti gli abbonati, che non conoscono i produttori.

Che cos'è il contratto di un servizio e che cosa si intende per discoverability?

Il contratto è l'insieme di tutti i messaggi che il servizio supporta, cioè la sua API; con REST si scompone in capability e poggia su un contratto uniforme (URI, metodi HTTP, media type). Discoverability significa che la descrizione del servizio può essere trovata e capita da persone e da altri servizi.

SOAP e WSDL: chi fa che cosa?

SOAP definisce il formato dei messaggi (envelope con header e body, in XML). WSDL definisce l'interfaccia del servizio: operazioni astratte (portType) e il loro legame con un protocollo concreto (binding).

Pro e contro dei microservizi?

Pro: rilascio e scalabilità indipendenti, team autonomi per area di business, libertà tecnologica, confini espliciti. Contro: tutto diventa un sistema distribuito, con latenza, comunicazione complessa, disponibilità ridotta dalle chiamate sincrone e dati da mantenere consistenti tra servizi.

Che differenza c'è tra IaaS, PaaS, SaaS e FaaS?

IaaS offre calcolo e memorizzazione, PaaS una piattaforma per sviluppare applicazioni, SaaS applicazioni pronte. FaaS esegue singole funzioni attivate da eventi, senza server da gestire, con scalabilità automatica e pagamento per invocazione: è il serverless.

Da quali lezioni viene questo capitolo

Slide [module-4.1] (modello canonico e limiti dei paradigmi tradizionali), [module-4.3] Service-Oriented Computing – An Overview e [Lab Notes] Message-Oriented Middlewares · 23 febbraio 2026 (osservazione su «write once, run anywhere» e thread) · 8 maggio (§ RPC, oggetti distribuiti e perché la trasparenza non funziona) · 18 maggio (assignment 4 e Java RMI; esempi della lab activity 13). Il modulo 4.3 è stato presentato nell'ultima lezione, di cui non c'è trascrizione: quella parte segue le slide. La mappa completa è in coverage-matrix.json.