Mentre procediamo con lo sviluppo dell’applicazione, tra varie cose, c’è una cosa principale di cui siamo meno preoccupati: il potere di calcolo. Per via dell’avvento dei provider di cloud, siamo meno preoccupati di gestire i data center. Tutto è disponibile in pochi secondi su richiesta. Ciò porta anche ad un aumento delle dimensioni dei dati. Big data viene generato e trasportato attraverso vari mezzi in singole richieste.
Con l’aumento delle dimensioni dei dati, si aggiungono attività come la serializzazione, la deserializzazione e i costi di trasporto. Anche se non siamo preoccupati per le risorse di calcolo, la latenza diventa un sovraccarico. Dobbiamo ridurre il trasporto. Sono stati sviluppati molti protocolli di messaggistica in passato per affrontare questo problema. SOAP era ingombrante e REST è una versione semplificata, ma abbiamo bisogno di un framework ancora più efficiente. Ed è qui che entrano in gioco le Chiamate di Procedure Remote — RPC.
In questo articolo, impareremo cos’è RPC e le varie implementazioni di RPC, con un focus su gRPC, che è l’implementazione di RPC di Google. Confronteremo anche REST con RPC e capiremo vari aspetti di gRPC, tra cui sicurezza, strumenti e molto altro.
Che cos’è RPC?
RPC sta per Remote Procedure Calls. La definizione è nel nome stesso. Le chiamate di procedura significano semplicemente chiamate di funzione/metodo; è la parola “Remote” che fa la differenza. E se potessimo fare una chiamata di funzione a distanza?
In poche parole, se una funzione risiede su un server e per essere invocata dal lato client, potremmo renderla semplice come una chiamata a metodo/funzione? Essenzialmente ciò che fa un RPC è dare l’illusione al client di invocare un metodo locale, ma in realtà invoca un metodo su una macchina remota che astratta le attività della rete. La bellezza di questo è che il contratto viene mantenuto molto rigoroso e trasparente (ne parleremo più avanti nell’articolo).
Passaggi coinvolti in una chiamata RPC:
Questo è ciò che un tipico processo REST sembra:
Le RPC riducono il processo a seguito:
Questo perché tutte le complicazioni associate alla creazione di una richiesta sono ora astratte da noi (ne parleremo nella generazione del codice). Tutto ciò che dobbiamo preoccuparci è i dati e la logica.
gRPC: Cosa, perché e come di esso
Finora abbiamo discusso RPC, che essenzialmente significa fare chiamate a funzioni/metodi in remoto, dandoci benefici come “definizione di contratto rigorosa,” “astrazione della trasmissione e conversione dei dati,” “riduzione della latenza,” e così via, di cui continueremo a discutere mentre procediamo con questo post. Ciò in cui vorremmo immergerci è una delle implementazioni di RPC. RPC è un concetto e gRPC è un framework basato su di esso.
Ci sono varie implementazioni di RPC. Sono:
-
gRPC (google)
-
Thrift (Facebook)
-
Finalge (Twitter)
La versione di RPC di Google è conosciuta come gRPC. È stata introdotta nel 2015 ed è in aumento di popolarità da allora. È uno dei meccanismi di comunicazione più scelti in un’architettura a microservizi.
gRPC utilizza protocol buffers (un formato di messaggio open-source) come metodo predefinito di comunicazione tra client e server. Inoltre, gRPC utilizza HTTP/2 come protocollo predefinito. Esistono quattro tipi di comunicazione supportati da gRPC:
-
Unary [comunicazione tipica tra client e server]
Veniamo ora al formato dei messaggi ampiamente utilizzato in gRPC — protocol buffers, detti anche protobufs. Un messaggio protobuf assomiglia a qualcosa di simile al seguente:
message Persona { |
In questo caso, ‘Persona’ è il messaggio che desideriamo trasferire (come parte di una richiesta/risposta) che ha i campi ‘nome’ (tipo stringa), ‘id’ (tipo stringa) e ‘email’ (tipo stringa). I numeri 1, 2, 3 rappresentano la posizione dei dati (come in ‘nome’, ‘id’, e ‘email’) quando vengono serializzati in formato binario.
Una volta che lo sviluppatore ha creato il file(i) Protocol Buffer con tutte le messaggi, possiamo utilizzare un compilatore di protocollo buffer (un binario) per compilare il file scritto di protocollo buffer, che genererà tutte le classi e i metodi utilità che sono necessari per lavorare con il messaggio. Ad esempio, come mostrato qui, il codice generato (a seconda della lingua scelta) apparirà come questo.
Come Definiamo i Servizi?
È necessario definire servizi che utilizzano i messaggi sopra per essere inviati/ricevuti.
Dopo aver scritto i tipi di messaggi di richiesta e risposta necessari, il passo successivo è scrivere il servizio stesso.
I servizi gRPC sono definiti anche in Protocol Buffers e utilizzano le parole chiave “service” e “RPC” per definire un servizio.
Dai un’occhiata al contenuto del file proto seguente:
message HelloRequest { message HelloResponse { service HelloService { |
Qui, HelloRequest e HelloResponse sono i messaggi e HelloService sta esponendo un RPC unario chiamato SayHello che prende HelloRequest come input e restituisce HelloResponse come output.
Come già detto, HelloService al momento contiene un solo RPC unario. Ma potrebbe contenere più di un RPC. Inoltre, può contenere una varietà di RPC (unario/flusso client-side/flusso server-side/Bidirezionale).
Per definire un RPC a flusso, tutto quello che devi fare è anteporre ‘stream ‘ prima dell’argomento di richiesta/risposta, Definizioni e codice generato per RPC a flusso.
Nel link del codice sopra:
-
streaming.proto: questo file è definito dall’utente
-
streaming.pb.go & streaming_grpc.pb.go: questi file sono generati automaticamente eseguendo il comando compilatore-proto.
gRPC Vs. REST
Abbiamo parlato abbastanza di gRPC. Inoltre, è stato menzionato REST. Ciò che abbiamo trascurato è stato discutere la differenza. Voglio dire, quando abbiamo un framework di comunicazione ben consolidato e leggero sotto forma di REST, perché c’era bisogno di cercare un altro framework di comunicazione? Comprendiamo meglio gRPC rispetto a REST, insieme ai pro e ai contro di ciascuno di essi.
Per poter confrontare, ciò che ci serve sono parametri. Quindi suddividiamo il confronto nei seguenti parametri:
-
Formato messaggio: protocol buffers vs JSON
-
La velocità di serializzazione e deserializzazione è molto migliore nel caso dei protocol buffers per tutte le dimensioni dei dati (piccole/medie/grandi). Risultati dei test di benchmark.
-
Dopo la serializzazione, JSON è leggibile dall’uomo mentre i protobufs (in formato binario) non lo sono. Non sono sicuro se questo sia uno svantaggio o meno perché a volte vorresti vedere i dettagli della richiesta nutool di sviluppo di Google o nei topic di Kafka e nel caso dei protobufs non puoi capire nulla.
-
-
Protocollo di comunicazione: HTTP 1.1 vs. HTTP/2T
-
REST si basa su HTTP 1.1; la comunicazione tra un client REST e un server richiederebbe una connessione TCP stabilita che a sua volta prevede un contatto a 3 vie. Quando riceviamo una risposta dal server dopo aver inviato una richiesta dal client, la connessione TCP non esiste più dopo di che. È necessario creare una nuova connessione TCP per elaborare un’altra richiesta. Questa creazione di una connessione TCP ad ogni richiesta aumenta la latenza.
-
Quindi gRPC, che si basa su HTTP 2, ha affrontato questa sfida mantenendo una connessione persistente. Dobbiamo ricordare che le connessioni persistenti in HTTP 2 sono diverse da quelle in web socket dove una connessione TCP viene intercettata e il trasferimento di dati non è monitorato. In una connessione gRPC, una volta stabilita una connessione TCP, viene riutilizzata per diverse richieste. Tutte le richieste dallo stesso client e server vengono multiplexate sulla stessa connessione TCP.
-
-
Solo preoccuparsi dei dati e della logica: la generazione di codice come prima classe
-
Le funzionalità di generazione di codice sono native a gRPC tramite il suo compilatore protoc integrato. Con le API REST, è necessario utilizzare uno strumento di terze parti come Swagger per generare automaticamente il codice per le chiamate API in vari linguaggi.
-
Nel caso di gRPC, esso semplifica il processo di marshalling/unmarshalling, la configurazione di una connessione e l’invio/ricezione di messaggi; ciò di cui dobbiamo preoccuparci è solo il dato che vogliamo inviare o ricevere e la logica.
-
-
Velocità di trasmissione
-
Dato che il formato binario è molto più leggero del formato JSON, la velocità di trasmissione in caso di gRPC è 7-10 volte più veloce rispetto a REST.
-
Caratteristica |
REST |
gRPC |
Protocollo di comunicazione |
Segue il modello richiesta-risposta. Può funzionare con entrambe le versioni di HTTP ma viene solitamente utilizzato con HTTP 1.1 |
Segue il modello client-risposta ed è basato su HTTP 2. Alcuni server hanno soluzioni alternative per farlo funzionare con HTTP 1.1 (attraverso gateway REST) |
Supporto dei browser |
Funziona ovunque |
Supporto limitato. È necessario utilizzare gRPC-Web, che è un’estensione per il web e si basa su HTTP 1.1 |
Struttura dei dati del payload |
Utilizza principalmente payload basati su JSON e XML per trasmettere dati |
Utilizza di default i protocol buffers per trasmettere i payload |
Generazione di codice |
È necessario utilizzare strumenti di terze parti come Swagger per generare il codice del client |
gRPC offre supporto nativo per la generazione di codice in varie lingue |
Memorizzazione nella cache delle richieste |
Facile da memorizzare nella cache le richieste sui lati client e server. La maggior parte dei client/server lo supporta nativamente (ad esempio tramite cookie) |
Non supporta di default la memorizzazione nella cache delle richieste/risposte |
Ancora, per il momento, gRPC non ha supporto per i browser poiché la maggior parte dei framework UI ha ancora supporto limitato o nullo per gRPC. Sebbene gRPC sia una scelta automatica in molti casi per la comunicazione tra microservizi interni, non è lo stesso per la comunicazione esterna che richiede l’integrazione UI.
Ora che abbiamo fatto un confronto tra entrambi i framework: gRPC e REST. Quale utilizzare e quando?
-
In un’architettura a microservizi con più microservizi leggeri, dove l’efficienza della trasmissione dei dati è fondamentale, gRPC sarebbe una scelta ideale.
-
Se la generazione di codice con supporto per più lingue è un requisito, gRPC dovrebbe essere il framework da preferire.
-
Grazie alle capacità di streaming di gRPC, applicazioni in tempo reale come trading o OTT ne trarrebbero vantaggio piuttosto che utilizzare il polling con REST.
-
Se la banda è un vincolo, gRPC offrirebbe una latenza e un throughput molto più bassi.
-
Se è richiesta una rapida evoluzione e iterazione ad alta velocità, REST dovrebbe essere l’opzione da prendere in considerazione.
Concetti di gRPC
Load Balancing
Anche se la connessione persistente risolve il problema della latenza, solleva un altro problema sotto forma di bilanciamento del carico. Poiché gRPC (o HTTP2) crea connessioni persistenti, anche in presenza di un bilanciamento del carico, il client stabilisce una connessione persistente con il server che si trova dietro il bilanciamento del carico. Questo è analogo a una sessione “sticky”.
Possiamo comprendere l’argomento o la sfida attraverso una demo. E il codice e i file di distribuzione sono disponibili qui: https://github.com/infracloudio/grpc-blog/tree/master/grpc-loadbalancing.
Dal codice base della demo sopra indicato, possiamo dedurre che la responsabilità del bilanciamento del carico ricade sul client. Ciò porta alla conclusione che l’aspetto positivo di gRPC, vale a dire la connessione persistente, non esiste con questa modifica. Tuttavia, gRPC può ancora essere utilizzato per i suoi altri vantaggi.
Leggi di più su bilanciamento del carico in gRPC.
Nel codice base della demo sopra, viene utilizzata/mostrata solo una strategia di bilanciamento del carico round-robin. Ma gRPC supporta anche un’altra strategia di bilanciamento del carico basata sul client OOB chiamata “pick-first”.
Inoltre, è supportato anche il bilanciamento del carico personalizzato sul lato client.
Contratto Pulito
In REST, il contratto tra client e server è documentato ma non rigido. Se torniamo indietro fino a SOAP, i contratti venivano esposti attraverso file wsdl. In REST esp
Con gRPC, il contratto, sia attraverso i file proto che attraverso gli stub generati da tali file, viene condiviso sia dal client che dal server. Questo è come effettuare una chiamata di funzione, ma in modalità remota. E poiché stiamo effettuando una chiamata di funzione, sappiamo esattamente cosa dobbiamo inviare e cosa ci aspettiamo come risposta. La complessità di stabilire connessioni con il client, prendere cura della sicurezza, della serializzazione-deserializzazione, ecc., viene astratta. Tutto ciò che ci interessa è i dati.
Considera il codice di seguito:
https://github.com/infracloudio/grpc-blog/tree/master/greet_app
Il client utilizza lo stub (codice generato dal file proto) per creare un oggetto client e invocare la chiamata di funzione remota:
```sh
import greetpb "github.com/infracloudio/grpc-blog/greet_app/internal/pkg/proto"
cc, err := grpc.Dial(“<server-address>”, opts)
if err != nil {
log.Fatalf("could not connect: %v", err)
}
c := greetpb.NewGreetServiceClient(cc)
res, err := c.Greet(context.Background(), req)
if err != nil {
log.Fatalf("error while calling greet rpc : %v", err)
}
```
Allo stesso modo, anche il server utilizza lo stesso stub (codice generato dal file proto) per ricevere l’oggetto richiesta e creare l’oggetto risposta:
```sh
import greetpb "github.com/infracloudio/grpc-blog/greet_app/internal/pkg/proto"
func (*server) Greet(_ context.Context, req *greetpb.GreetingRequest) (*greetpb.GreetingResponse, error) {
// fare qualcosa con 'req'
return &greetpb.GreetingResponse{
Result: result,
}, nil
}
```
Entrambi stanno utilizzando lo stesso stub generato dal file proto che risiede qui.
E lo stub è stato generato utilizzando il comando del compilatore proto riportato di seguito.
```sh
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative internal/pkg/proto/*.proto
```
Sicurezza
L’autenticazione e l’autorizzazione gRPC operano su due livelli:
-
L’autenticazione/autorizzazione a livello di chiamata viene generalmente gestita tramite token applicati nei metadati al momento della chiamata. Esempio di autenticazione basata su token.
-
L’autenticazione a livello di canale utilizza un certificato client applicato a livello di connessione. Può anche includere credenziali di autenticazione/autorizzazione a livello di chiamata da applicare automaticamente a ogni chiamata sul canale. Esempio di autenticazione basata su certificato.
Entrambi o uno solo di questi meccanismi possono essere utilizzati per aiutare a proteggere i servizi.
Middleware
In REST, utilizziamo il middleware per vari scopi come:
-
Limitazione della frequenza
-
Convalida pre/post richiesta/risposta
-
Affrontare minacce alla sicurezza
Possiamo ottenere lo stesso con gRPC. La terminologia è diversa in gRPC — sono chiamati interceptors, ma svolgono attività simili.
Nel ramo middleware del codice base ‘greet_app’, abbiamo integrato interceptors di logger e Prometheus.
Guarda come gli interceptors sono configurati per utilizzare i pacchetti Prometheus e logging qui.
Ma possiamo integrare altri pacchetti negli interceptors per scopi come prevenire il panico e il recupero (per gestire eccezioni), tracing, persino autenticazione, e così via.
Middleware supportati dal framework gRPC.
Imballaggio, versionamento e pratiche di codice dei file Proto
Imballaggio
Seguiamo il ramo di packaging.
Inizia con ‘Taskfile.yaml’, la task ‘gen-pkg’ indica ‘protoc –proto_path=packaging packaging/*.proto –go_out=packaging’. Ciò significa che ‘protoc’ (il compilatore) convertirà tutti i file in ‘packaging/*.proto’ nei loro equivalenti file ‘go’, come indicato dall’opzione ‘–go_out=packaging’, direttamente nella directory ‘packaging’.
In secondo luogo, nel file ‘processor.proto’, sono stati definiti 2 messaggi, ovvero ‘CPU’ e ‘GPU’. Mentre CPU è un messaggio semplice con 3 campi di tipi di dati incorporati, GPU, d’altra parte, ha un tipo di dati personalizzato chiamato ‘Memory’. ‘Memory’ è un messaggio separato e definito in un file completamente diverso.
Quindi, come si utilizza il messaggio ‘Memory’ nel file ‘processor.proto’? Utilizzando import.
Anche se provi a generare un file proto eseguendo la task ‘gen-pkg’ dopo aver menzionato l’import, genererai un errore. Poiché per default ‘protoc’ presuppone che entrambi i file ‘memory.proto’ e ‘processor.proto’ siano in pacchetti diversi. Quindi è necessario specificare lo stesso nome di package in entrambi i file.
L’opzionale ‘go_package’ indica al compilatore di creare un nome di package come ‘pb’ per i file go. Se fossero stati creati file proto per un’altra lingua, il nome del package sarebbe stato ‘laptop_pkg’.
Versioning
-
Ci possono essere due tipi di modifiche in gRPC: quelle che rompono e quelle che non rompono.
-
Le modifiche non rotture includono l’aggiunta di un nuovo servizio, l’aggiunta di un nuovo metodo a un servizio, l’aggiunta di un campo al proto di richiesta o risposta e l’aggiunta di un valore all’enum
-
Le modifiche che rompono, come rinominare un campo, cambiare il tipo di dati di un campo, il numero di campo, rinominare o rimuovere un pacchetto, servizio o metodi, richiedono la versionamento dei servizi
-
Facoltativo packaging.
Pratiche di codifica
-
Il messaggio di richiesta deve terminare con la richiesta `CreateUserRequest`
-
Il messaggio di risposta deve terminare con la richiesta `CreateUserResponse`
-
Nel caso in cui il messaggio di risposta sia vuoto, puoi utilizzare un oggetto vuoto `CreateUserResponse` oppure il `google.protobuf.Empty`
-
Il nome del pacchetto deve avere senso e deve essere versionato, ad esempio: pacchetto `com.ic.internal_api.service1.v1`
Strumenti
L’ecosistema gRPC supporta una serie di strumenti per facilitare le attività non di sviluppo come la documentazione, il gateway REST per un server gRPC, l’integrazione di validatori personalizzati, il linting, ecc. Ecco alcuni strumenti che possono aiutarci a raggiungere lo stesso obiettivo:
-
protoc-gen-grpc-gateway — plugin per creare un gateway REST API gRPC. Consente di utilizzare gli endpoint gRPC come endpoint REST API e esegue la traduzione da JSON a proto. Fondamentalmente, definisci un servizio gRPC con alcune annotazioni personalizzate e rende queste metodi gRPC accessibili tramite REST utilizzando richieste JSON.
-
protoc-gen-swagger — un plugin ausiliario per grpc-gateway. È in grado di generare swagger.json sulla base delle annotazioni personalizzate necessarie per il gateway gRPC. È possibile quindi importare quel file nel client REST del vostro scelto (come Postman) e eseguire chiamate API REST ai metodi che avete esposto.
-
protoc-gen-grpc-web — un plugin che permette al front end di comunicare con il backend utilizzando chiamate gRPC. Un post separato su questo argomento verrà pubblicato in futuro.
-
protoc-gen-go-validators — un plugin che consente di definire regole di validazione per i campi dei messaggi proto. Genera un metodo Validate() error per i messaggi proto che puoi richiamare in GoLang per verificare se il messaggio corrisponde alle tue aspettative predefinite.
-
https://github.com/yoheimuta/protolint — un plugin per aggiungere regole di lint ai file proto
Testing Utilizzando POSTMAN
A differenza del testing delle API REST con Postman o strumenti equivalenti come Insomnia, non è molto comodo testare i servizi gRPC.
Nota: i servizi gRPC possono anche essere testati dalla CLI utilizzando strumenti come evans-cli. Ma per questo è necessaria la riflessione (se non abilitata, è richiesto il percorso del file proto) da abilitare nei server gRPC. Modifiche da apportare per abilitare la riflessione e come entrare nella modalità repl di evans-cli. Dopo aver entrato nella modalità repl di evans-cli, i servizi gRPC possono essere testati direttamente dalla CLI e il processo è descritto nella pagina github di evans-cli.
Postman ha una versione beta per testare i servizi gRPC.
Ecco i passaggi su come puoi farlo:
-
Apri Postman
-
Vai su ‘APIs’ nella barra laterale a sinistra
-
Fai clic sul segno ‘+’ per creare una nuova API:
-
Nella finestra popup, inserisci ‘Nome’, ‘Versione’ e ‘Dettagli Schema’ e fai clic su Crea [a meno che non abbia bisogno di importare da alcune fonti come github/bitbucket]. Questo passaggio è rilevante se vuoi copiare-incollare il contratto proto.
5. Viene creata la tua API come mostrato di seguito. Clicca sulla versione ‘1.0.0’, vai alla definizione e inserisci il tuo contratto proto.
-
Ricorda che l’importazione non funziona qui, quindi sarebbe meglio tenere tutti i protos dipendenti in un unico posto.
-
I passaggi precedenti aiuteranno a conservare contratti per un uso futuro.
-
Quindi clicca su ‘Nuovo’ e seleziona ‘Richiesta gRPC’:
-
Inserisci l’URI e scegli il proto dall’elenco delle API salvate:
-
Inserisci il tuo messaggio di richiesta e ‘Invoca’:
Nei passaggi precedenti abbiamo capito il processo per testare le nostre API gRPC tramite POSTMAN. Il processo per testare gli endpoint gRPC è diverso da quello degli endpoint REST utilizzando POSTMAN. Una cosa da ricordare è che mentre si crea e si salva il contratto proto come in #5, tutte le definizioni di messaggi e servizi proto devono essere nello stesso posto. Non essendoci la possibilità di accedere ai messaggi proto tra versioni in POSTMAN.
Conclusione
In questo post, abbiamo sviluppato un’idea su RPC, abbiamo tracciato parallelismi con REST e ne abbiamo discussi le differenze, poi siamo passati a discutere un’implementazione di RPC, cioè gRPC sviluppato da Google.
gRPC come framework può essere cruciale, specialmente per l’architettura basata su microservizi per la comunicazione interna. Può essere utilizzato anche per la comunicazione esterna, ma richiederà un gateway REST. gRPC è indispensabile per applicazioni di streaming e in tempo reale.
Il modo in cui Golang si sta dimostrando come linguaggio di scripting lato server, gRPC si sta dimostrando come framework di comunicazione de-facto.
Source:
https://dzone.com/articles/understanding-grpc-concepts-use-cases-amp-best-pra