Il sistema gRPC, precursore della comunicazione client-server del futuro
La tecnologia di rete continua a fare passi da gigante. Per soddisfare le crescenti esigenze all’interno dei sistemi informatici distribuiti, per le Remote Procedure Calls è stato sviluppato un nuovo sistema denominato gRPC. La “g” iniziale sta per Google, che ha fornito un contributo determinante allo sviluppo di questa soluzione. In questo articolo spieghiamo cos’è gRPC, come funziona e dove viene utilizzato.
Cos’è gRPC?
gRPC è un moderno sistema di remote procedure call che, grazie a tecniche di processo innovative, rende possibile una comunicazione particolarmente efficiente all’interno delle architetture client-server distribuite. Come il predecessore RPC, anche gRPC agisce a livello di processo. Caratteristica distintiva della comunicazione interprocesso tramite gRPC è il principio della trasparenza: la collaborazione di istanze (talvolta molto) lontane è talmente stretta e fluida che non si avverte alcuna differenza rispetto a una comunicazione locale tra processi interni alla stessa macchina.
Il sistema gRPC è stato inizialmente sviluppato da Google nel 2015. Oggi la Cloud Native Computing Foundation svolge un ruolo di primo piano nella diffusione e nel perfezionamento di questa tecnica di comunicazione. Il sistema gRPC è di tipo open source, vale a dire che il testo sorgente è accessibile a tutti ed eventuali modifiche e migliorie del codice da parte di terzi sono non solo consentite, ma auspicabili.
Il trasporto dei flussi di dati tra computer distanti gli uni dagli altri è effettuato solitamente da gRPC tramite protocollo HTTP/2, mentre la strutturazione e l’elaborazione dei dati sono affidate a buffer di protocollo. Questi ultimi sono salvati sotto forma di semplici file di testo con l’estensione .proto.
gRPC è spesso definito come framework. Un framework si contraddistingue per il fatto che mette a disposizione degli sviluppatori una cornice di programmazione, facendo così risparmiare tempo e lavoro. L’impalcatura standard di gRPC contiene infatti già diverse funzioni ed elementi che non devono essere riprogrammati ogni volta ex novo. Il framework gRPC definisce inoltre interfacce standardizzate per determinate origini (ad es. banche dati).
Come funziona gRPC: poliglotta ed efficiente grazie a buffer di protocollo e HTTP/2
Nel sistema gRPC, i buffer di protocollo (Protobuf) svolgono molteplici funzioni: fungono da Interface Definition Language (linguaggio di definizione dell’interfaccia, IDL) e descrivono un’interfaccia in modo indipendente dal linguaggio. Non sono pertanto legati a un linguaggio di programmazione specifico (ad esempio Java o C). Inoltre definiscono i servizi da utilizzare e le funzioni rese disponibili. Per ciascuna funzione è possibile specificare quali parametri vengono inviati con una richiesta e quale valore di risposta ci si può aspettare.
Dalla prospettiva della comunicazione remota, i buffer di protocollo rappresentano il formato fondamentale di interscambio di messaggi, che definisce le strutture, le tipologie e gli oggetti dei messaggi. I messaggi permettono al client e al server di “capirsi” e di operare come un’unica unità funzionale e quanto più possibile efficiente anche a grandi distanze.
Nel caso di una richiesta a una banca dati (ad es. “Ricerca la disponibilità dell’articolo a magazzino”), l’esecuzione di una chiamata gRPC si presenta nel modo seguente:
- prima che la ricerca possa essere effettivamente elaborata, sono necessarie alcune operazioni preparatorie. Sul lato server vengono dapprima implementati, sulla base del buffer di protocollo, un servizio e un server gRPC. Il client richiedente genera da parte sua uno stub adatto. Quando lo stub è disponibile, l’applicazione client richiama la funzione corrispondente (“Query di ricerca della disponibilità articolo”) nello stub.
- Successivamente, la richiesta viene inviata al server tramite la rete. Dopo avere ricevuto la richiesta con l’ausilio di un’opportuna interfaccia di servizio, il server gRPC avvia la ricerca vera e propria del prodotto nella banca dati.
- In seguito, il server invia la risposta al client stub, che inoltra il valore di risposta all’istanza richiedente iniziale (ad es. “Numero degli articoli trovati”).
Le applicazioni sul lato client e server possono essere scritte con linguaggi di programmazione diversi. gRPC supera queste limitazioni grazie a interfacce e meccanismi di traduzione speciali.
Per il trasporto di andata e ritorno dei flussi di dati tra le macchine (proto request e proto response), HTTP/2 viene integrato in speciali protocolli di rete come TCP/IP o UDP/IP. Gli stream trasferiscono dati binari compatti, che vengono creati nell’ambito della serializzazione (marshalling) tipica per le chiamate di procedura remote. Affinché le strutture di dati totalmente astratte possano essere elaborate sul lato client e server in ulteriori passaggi, il messaggio trasmesso viene in seguito deserializzato (unmarshalling).
Il workflow gRPC e i primi passaggi tramite buffer di protocollo
Il workflow gRPC si articola tipicamente in quattro passaggi:
- Definizione del contratto di servizi per la comunicazione interprocesso: vengono definiti i servizi da creare nonché i parametri fondamentali e le tipologie di risposta che possono essere richiamati in remoto.
- Generazione del codice gRPC dal file .proto: speciali compilatori (strumenti riga di comando detti "protoc") generano, a partire da file .proto salvati, il codice operativo con le classi corrispondenti per il linguaggio di destinazione desiderato (ad es. C++, Java).
- Implementazione dei servizi nel linguaggio scelto.
- Creazione del client stub che richiama il servizio; successivamente la richiesta o le richieste vengono elaborate tramite server e client.
Nel caso di una query su banca dati con la quale si ricerca un prodotto a magazzino (inventory), i primi passaggi concreti nella sintassi effettiva dei buffer di protocollo (versione: proto3) si presentano nel modo seguente:
syntax = "proto3";
package grpc_service;
import "google/protobuf/wrappers.proto";
service InventoryService {
rpc getItemByName(google.protobuf.StringValue) returns (Items);
rpc getItemByID(google.protobuf.StringValue) returns (Item);
rpc addItem(Item) returns (google.protobuf.BoolValue);
}
message Items {
string itemDesc = 1;
repeated Item items = 2;
}
message Item {
string id = 1;
string name = 2;
string description = 3;
}
Nell’esempio sopra, la query sulla banca dati utilizza speciali “librerie wrapper” del framework gRPC che mettono a disposizione procedure di traduzione rilevanti pre-programmate, importate all’inizio della procedura. Nelle architetture client-server di tipo diverso e in cui si utilizzano linguaggi differenti, queste procedure fanno sì che interfacce altrimenti incompatibili possano comunicare le une con le altre. Ad esempio, vengono generate le classi necessarie per la lettura e la scrittura dei messaggi.
La figura seguente mostra come la definizione dei servizi (inventory.proto) regola la query su una banca dati in un’architettura client-server:
HTTP/2: streaming con prestazioni ottimizzate
In gRPC, il protocollo HTTP/2 riveste un ruolo fondamentale in quanto permette il trasferimento di dati binari compatti e rende particolarmente efficiente lo scambio di messaggi e dati. Il protocollo di rete mette a disposizione quattro varianti di Remote Procedure Calls:
1. le RPC unarie sono il modello gRPC più semplice. Con queste RPC, il client invia al server un’unica richiesta. Come avviene in una normale chiamata a una funzione, riceve quindi un’unica risposta.
Esempio: rpc SayHello (HelloRequest) restituisce risposta (HelloResponse)
2. Con le RPC di streaming del server può essere realizzato uno scambio di messaggi più complesso all’interno di una singola chiamata RPC. Per prima cosa, il client invia una richiesta al server. Successivamente riceve come risposta dal server uno stream contenente una sequenza di messaggi più articolata (compilazione di messaggi efficiente all’interno di una singola chiamata RPC). Il client continua la lettura dello stream fino a che non ci sono più messaggi.
Esempio: rpc LotsOfReplies (HelloRequest) restituisce (stream HelloResponse)
3. Con le RPC di streaming del client il processo è invertito: il client scrive una sequenza di messaggi e la invia tramite stream al server. Dopo avere completato i messaggi, il client attende che il server li legga e fornisca la risposta. Anche in questo caso, la compilazione dei messaggi avviene all’interno di una singola chiamata RPC.
Esempio: rpc LotsOfGreetings (stream HelloRequest) restituisce (HelloResponse)
4. Le RPC di streaming bidirezionale sono la forma di comunicazione più complessa, in cui entrambi i lati inviano una sequenza di messaggi. I due stream di dati lavorano in modo indipendente l’uno dall’altro, così che client e server possono leggere e scrivere in qualunque sequenza: ad esempio il server può attendere che tutti i messaggi siano arrivati prima di scrivere la sua risposta. Può tuttavia anche leggere un messaggio alla volta e scrivere la risposta corrispondente. È inoltre possibile un’altra combinazione dei processi di lettura e scrittura.
Esempio: rpc BidiHello (stream HelloRequest) restituisce (stream HelloResponse)
Le varianti da 2 a 4 stabiliscono, con richieste nidificate, un multiplexing particolarmente efficiente in cui, all’interno di un’unica connessione TCP, vengono riuniti più segnali che possono essere trasmessi simultaneamente tramite la rete. I blocchi nel traffico dati vengono superati grazie al potente protocollo HTTP/2.
Vantaggi e svantaggi di gRPC
gRPC offre numerosi vantaggi: si tratta di una tecnologia relativamente semplice da implementare, poiché crea i file .proto utilizzando un IDL facile che può essere appreso in tempi relativamente rapidi. L’utente può quindi ampliare con facilità i programmi creati autonomamente con una potente interfaccia gRPC e trasferire anche file di grandi dimensioni con una velocità molto più elevata.
Inoltre, gRPC è ampiamente testato e altamente scalabile, ed è quindi utilizzabile anche per comunicazioni più complesse e di maggiore portata, ad esempio nelle architetture client-server collegate in rete a livello mondiale. Il protocollo HTTP/2, inoltre, aumenta l’efficienza non solo attraverso il multiplexing e lo streaming bidirezionale: sono infatti possibili anche compressioni degli header che riducono in misura significativa il volume dei dati delle richieste e delle risposte all’interno della rete.
La struttura stratificata e fortemente standardizzata del framework gRPC riduce inoltre il lavoro necessario per la programmazione. Gli sviluppatori possono così concentrarsi in modo prioritario sull’implementazione dei metodi. Se sono necessarie modifiche, i programmatori e gli sviluppatori di sistema beneficiano della possibilità di accedere liberamente ai codici sorgente.
I buffer di protocollo e i relativi compilatori protobuf permettono inoltre una comunicazione illimitata: sistemi operativi diversi, componenti differenti di architetture client-server e linguaggi di programmazione diversi non rappresentano più un ostacolo. Così, un software scritto in C può comunicare ad esempio con un software Java. I compilatori protobuf sono oggi disponibili per molti altri linguaggi, ad esempio per C#, C++, Go, Objective-C, Java, Python, Node.js, Android Java, Swift, Ruby e PHP.
Un altro vantaggio di gRPC è costituito dalla sua flessibilità. Il sistema può essere utilizzato ad esempio sia per lo scambio di dati di microservizi, sia per app mobili che condividono i propri dati con dei server. Un altro punto a favore di gRPC è il fatto che il protocollo permette l’implementazione delle odierne tecnologie di sicurezza. gRPC dispone di un’autenticazione integrata e richiede l’utilizzo di SSL/TLS per l’autenticazione e la crittografia dello scambio.
Tra i punti deboli di gRPC ricordiamo i seguenti: in base allo stato attuale della tecnica, i test delle interfacce gRPC sono passibili di miglioramento. I messaggi gRPC codificati con protobuf non sono leggibili dagli esseri umani. Di conseguenza, l’analisi del traffico dati e, in particolare, la ricerca e la risoluzione degli errori (debug) richiedono l’utilizzo di ulteriori istanze per trasporre il codice in una forma comprensibile e localizzare così le origini degli errori. Altri svantaggi derivano dal collegamento in rete di architetture client-server distribuite. gRPC collega computer distanti tra loro e risulta pertanto più vulnerabile rispetto ai sistemi locali. La sua efficienza dipende dalla disponibilità di una rete stabile e potente; inoltre, la rete, il traffico dati, i protocolli di trasmissione nonché client e server devono essere sicuri da potenziali attacchi di hacker. Un ulteriore svantaggio è rappresentato dal fatto che gRPC non supporta il multicasting.
gRPC e REST a confronto
Grazie alle sue caratteristiche positive, gRPC rappresenta un’alternativa concorrenziale a REST (Representational State Transfer). Quest’ultimo è adatto in particolare per servizi semplici, mentre gRPC mostra i suoi punti di forza con interfacce (API) e servizi più complessi. Per lo scambio di dati tra applicazioni, REST utilizza generalmente il formato JSON, che è basato su file di testo e grava quindi pesantemente sulle risorse di rete.
Per contro, grazie ai buffer di protocollo e a HTTP/2, gRPC può organizzare un flusso di dati molto più compatto. Grazie alla serializzazione e alla binarizzazione dei dati, lo spazio di memoria necessario risulta pertanto ridotto quasi del 70 percento rispetto ad esempio a JSON. Inoltre lo streaming bidirezionale, che funziona senza blocchi per lo scambio dei dati, garantisce rispetto a REST significativi vantaggi in termini di prestazioni e velocità.
Attualmente la collaborazione con le web app è ancora migliorabile, poiché spesso queste ultime non sono ottimizzate per l’utilizzo di buffer di protocollo, per l’“approccio in funzione del contratto”, tipico di gRPC, nonché per le API contract first del framework gRPC. Penalizzante per la collaborazione con le applicazioni web è il fatto che nessun servizio gRPC può ancora essere richiamato direttamente da un browser.
Nel caso di REST questo problema non sussiste poiché, contrariamente al più recente HTTP/2, il classico protocollo HTTP è supportato da tutti i browser. Tuttavia, questa lacuna può essere colmata con uno sforzo accettabile, così che lo stesso servizio può essere utilizzato per un’applicazione web e per un’app mobile. Una possibilità al riguardo è rappresentata da gRPC-Web. Gli sviluppatori di gRPC stanno lavorando a ulteriori soluzioni per semplificare il più possibile la collaborazione tra gRPC e i tool basati sul web.
Dove viene utilizzato gRPC?
gRPC è particolarmente indicato per realizzare una comunicazione interprocesso efficiente nelle architetture client-server distribuite in cui risultano importanti una bassa latenza e un elevato volume di dati e messaggi. All’interno dei centri di calcolo e fra centri di calcolo distanti gli uni dagli altri, gRPC viene spesso utilizzato per collegare servizi o microservizi. Poiché i tool gRPC supportano la maggior parte dei più comuni linguaggi di sviluppo sono spesso utilizzati in ambienti multilingue.
La velocità, l’efficienza e il plurilinguismo depongono a favore dell’utilizzo di gRPC in ambito mobile e nella comunicazione delle app. gRPC regola sempre più l’ultimo miglio dell’elaborazione dati distribuita, collegando dispositivi, applicazioni mobili e browser con servizi backend.
Il potente streaming tramite HTTP/2 fa di gRPC il sistema predestinato per la comunicazione punto a punto in tempo reale. L’Internet of things (tecnologie smart home) trae vantaggio dal procedimento a basso consumo di risorse, in quanto regola sempre più la comunicazione di low resource client. In ragione dei vantaggi in termini di prestazioni e della semplicità di sviluppo, in futuro questa tecnologia di comunicazione potrebbe conquistare un ruolo di primo piano nel cloud, limitando l’attuale predominanza di REST. gRPC viene attualmente considerato anche un’alternativa a XML (Extensible Markup Language).
XML è un formato frequentemente utilizzato per lo scambio di dati e la memorizzazione strutturata di informazioni.