saverioriotto.it

Guida completa all'utilizzo del protocollo gRPC con Maven

Scopri come utilizzare il potente protocollo gRPC in combinazione con Maven per creare un sistema efficiente, semplificando la comunicazione tra client e server con serializzazione Protocol Buffers e integrando funzionalità avanzate.

Guida completa all'utilizzo del protocollo gRPC con Maven

In questo tutorial, ti guiderò attraverso alcuni passaggi necessari, ad utilizzare il protocollo gRPC in un progetto Maven.

gRPC è un framework ad alte prestazioni sviluppato da Google per la comunicazione tra servizi distribuiti. Utilizzeremo Maven come sistema di gestione delle dipendenze per semplificare il processo di configurazione del progetto.

Vedi anche: "gRPC: panoramica e principali differenze con il servizio REST"

Preparazione del progetto

Crea un nuovo progetto Maven utilizzando il tuo IDE preferito o tramite la riga di comando.

Aggiungi le dipendenze necessarie nel file pom.xml del progetto. Le dipendenze necessarie per utilizzare gRPC sono:

   <dependency>
      <groupId>io.grpc</groupId>
      <artifactId>grpc-netty-shaded</artifactId>
      <version>1.53.0</version>
    </dependency>
    <dependency>
      <groupId>io.grpc</groupId>
      <artifactId>grpc-protobuf</artifactId>
      <version>1.53.0</version>
    </dependency>
    <dependency>
      <groupId>io.grpc</groupId>
      <artifactId>grpc-stub</artifactId>
      <version>1.53.0</version>
    </dependency>
    <dependency>
      <groupId>javax.annotation</groupId>
      <artifactId>javax.annotation-api</artifactId>
      <version>1.3.2</version>
    </dependency>

Definizione del servizio e dei messaggi

Crea un file .proto per definire il servizio e i messaggi necessari. Nel nostro esempio utilizziamo l’anagrafica dei film come esempio. Quindi puoi chiamarlo film.proto.

Ecco un esempio di definizione di un servizio per la gestione dell'anagrafica film:

syntax = "proto3";

package film;

service FilmService {
  rpc GetFilm(GetFilmRequest) returns (FilmResponse) {}
  rpc AddFilm(AddFilmRequest) returns (FilmResponse) {}
}

message GetFilmRequest {
  string filmId = 1;
}

message AddFilmRequest {
  string title = 1;
  string director = 2;
  int32 year = 3;
}

message FilmResponse {
  string title = 1;
  string director = 2;
  int32 year = 3;
}

Il file .proto è un file di definizione utilizzato da gRPC per definire il servizio e i messaggi scambiati tra il client e il server. È il file di contratto tra le due parti coinvolte nella comunicazione.

Ecco alcune informazioni sullo scopo e l'utilità del file .proto:

 - Definizione del servizio: Nel file .proto, puoi definire il servizio che verrà offerto dal server. Il servizio definisce le operazioni che il client può richiedere al server. Ad esempio, nel tutorial, abbiamo definito il servizio FilmService con due operazioni: GetFilm per ottenere i dettagli di un film e AddFilm per aggiungere un nuovo film.

 - Definizione dei messaggi: Il file .proto viene utilizzato anche per definire i messaggi scambiati tra il client e il server. I messaggi rappresentano i dati strutturati che vengono inviati tra le due parti. Nel tutorial, abbiamo definito tre messaggi: GetFilmRequest per la richiesta di ottenere i dettagli di un film, AddFilmRequest per la richiesta di aggiungere un nuovo film e FilmResponse per la risposta contenente i dettagli di un film.

 - Serializzazione dei dati: Il file .proto specifica la struttura dei dati e la loro serializzazione. gRPC utilizza Protocol Buffers, un formato binario efficiente per la serializzazione dei dati. Con la definizione nel file .proto, gRPC genererà automaticamente il codice sorgente per i messaggi, consentendo al client e al server di serializzare e deserializzare i dati in modo coerente.

 - Interoperabilità tra i linguaggi di programmazione: Una delle grandi caratteristiche di gRPC è la sua capacità di supportare client e server scritti in diversi linguaggi di programmazione. Utilizzando il file .proto, è possibile generare automaticamente il codice sorgente per i vari linguaggi di programmazione supportati da gRPC, consentendo una facile integrazione e comunicazione tra le diverse parti del sistema distribuito.

Generazione dei codici sorgenti

Per generare i codici sorgenti gRPC dal file .proto, apri il terminale nella directory del progetto e esegui il seguente comando:

protoc --java_out=src/main/java film.proto

Se non hai protoc (il compilatore di Protocol Buffers) disponibile nel tuo ambiente, ci sono alcune alternative che puoi considerare:

 - Utilizzare un servizio online: consentono di compilare file .proto senza la necessità di installare protoc localmente.

 - Utilizzare un plugin Maven: offre la funzionalità di compilazione dei file .proto durante il processo di build del progetto. Questi plugin integrano il compilatore di Protocol Buffers nel ciclo di vita di Maven e generano automaticamente i codici sorgenti Java.

Scegli l'opzione più adatta al tuo caso e alla tua configurazione. Ricorda che l'obiettivo principale è generare i codici sorgenti Java dai file .proto in modo che tu possa utilizzarli nel tuo progetto Maven.

Implementazione del server

Crea una classe che estende l'interfaccia generata dal servizio gRPC. Nel nostro esempio, la chiameremo FilmServiceImpl. Vediamo quindi l’implementazione:

public class FilmServiceImpl extends FilmServiceGrpc.FilmServiceImplBase {
    @Override
    public void getFilm(ProductService.GetFilmRequest request, StreamObserver responseObserver) {
        // Logica per ottenere i dettagli di un film
        // ...
        System.out.println("Get film con id -> "+request.getFilmId());

        ProductService.FilmResponse response = ProductService.FilmResponse.newBuilder()
                .setTitle("Titolo del film "+request.getFilmId())
                .setDirector("Regista del film "+request.getFilmId())
                .setYear(2022)
                .build();
        responseObserver.onNext(response);
        responseObserver.onCompleted();
    }

    @Override
    public void addFilm(ProductService.AddFilmRequest request, StreamObserver responseObserver) {
        // Logica per aggiungere un nuovo film all'anagrafica
        // ...

        System.out.println("Add film "+request.getTitle()+" "+request.getDirector()+" "+request.getYear());

        ProductService.FilmResponse response = ProductService.FilmResponse.newBuilder()
                .setTitle(request.getTitle())
                .setDirector(request.getDirector())
                .setYear(request.getYear())
                .build();
        responseObserver.onNext(response);
        responseObserver.onCompleted();
    }
}

Il server viene implementato nella classe FilmServiceImpl, che estende FilmServiceGrpc.FilmServiceImplBase, un'interfaccia generata automaticamente dal compilatore gRPC.

Nel metodo getFilm, viene ricevuta una richiesta di ottenere i dettagli di un film identificato da un filmId.

Nell'esempio fornito, viene creata una risposta fittizia con un titolo, un regista e un anno.

Nel metodo addFilm, viene ricevuta una richiesta per aggiungere un nuovo film con un title, un director e un year.

Questa logica potrebbe essere personalizzata per ottenere i dettagli del film oppure aggiungere un film su un sistema di archiviazione o un database, ma nel nostro esempio ci fermiamo alla semplice comunicazione.

Implementazione del client

Crea una classe per il client che interagirà con il servizio gRPC. Nel nostro esempio, la chiameremo FilmServiceClient. Ecco un esempio di implementazione:

public class FilmServiceClient {
    private final FilmServiceGrpc.FilmServiceBlockingStub blockingStub;

    public FilmServiceClient(String host, int port) {
        ManagedChannel channel = ManagedChannelBuilder.forAddress(host, port)
                .usePlaintext()
                .build();
        blockingStub = FilmServiceGrpc.newBlockingStub(channel);
    }

    public ProductService.FilmResponse getFilm(String filmId) {
        ProductService.GetFilmRequest request = ProductService.GetFilmRequest.newBuilder()
                .setFilmId(filmId)
                .build();
        return blockingStub.getFilm(request);
    }

    public ProductService.FilmResponse addFilm(String title, String director, int year) {
        ProductService.AddFilmRequest request = ProductService.AddFilmRequest.newBuilder()
                .setTitle(title)
                .setDirector(director)
                .setYear(year)
                .build();
        return blockingStub.addFilm(request);
    }
}

Nel costruttore, viene creato un canale di comunicazione gRPC con il server specificando l'indirizzo host e la porta port. Nel nostro esempio, viene utilizzato localhost come host e la porta 50051.

Il client fornisce due metodi principali:

- getFilm prende un filmId e invia una richiesta al server per ottenere i dettagli del film corrispondente. Restituisce una risposta che contiene il titolo, il regista e l'anno del film.

- addFilm prende il title, il director e l'year di un nuovo film e invia una richiesta al server per aggiungerlo all'anagrafica. Restituisce una risposta che contiene i dati del film appena aggiunto.

Esecuzione del server e del client

Nel metodo main del Server e de Client, vengono avviati i servizi.

Per avviare il server, viene creata un'istanza di Server utilizzando ServerBuilder. Viene specificata la porta su cui il server ascolterà le richieste (nel nostro caso, 50051) e viene aggiunto il servizio FilmServiceImpl al server. Infine, il server viene avviato e viene visualizzato un messaggio di conferma.

public class FilmServer {

    private static final int PORT = 50051;

    public static void main(String[] args) throws IOException, InterruptedException {
        Server server = ServerBuilder.forPort(PORT)
                .addService(new FilmServiceImpl())
                .build();

        server.start();
        System.out.println("Server started on port " + PORT);

        server.awaitTermination();
    }
}
//Output
//> Server started on port 50051

Per utilizzare il client, viene creata un'istanza di FilmServiceClient specificando l'indirizzo del server (localhost) e la porta (50051).

public class FilmClient
{
    public static void main( String[] args )
    {
        FilmServiceClient client = new FilmServiceClient("localhost", 50051);

        // Utilizza il client per ottenere i dettagli di un film
        ProductService.FilmResponse film = client.getFilm("123");
        System.out.println("Dettagli del film: " + film.getTitle() + ", " + film.getDirector() + ", " + film.getYear());

        // Utilizza il client per aggiungere un nuovo film
        ProductService.FilmResponse newFilm = client.addFilm("Titolo del nuovo film", "Regista del nuovo film", 2023);
        System.out.println("Nuovo film aggiunto: " + newFilm.getTitle() + ", " + newFilm.getDirector() + ", " + newFilm.getYear());
    }
}

Il client viene quindi utilizzato per invocare i metodi getFilm e addFilm, passando i dati appropriati e visualizzando i risultati ottenuti.

Conclusione

L'utilizzo di gRPC insieme a Maven ci consente di costruire sistemi scalabili e performanti, migliorando l'efficienza nella comunicazione tra client e server. Abbiamo esplorato l'utilizzo del protocollo gRPC in combinazione con Maven.

Abbiamo imparato come definire il servizio e i messaggi utilizzando il file .proto, generare i codici sorgenti Java necessari e implementare le operazioni di servizio. Speriamo che questa guida ti abbia fornito una base solida per iniziare a utilizzare gRPC in combinazione con Maven per creare potenti servizi.

Adesso non ti rimane altro che sperimentare con le diverse funzionalità offerte da questa tecnologia.

Il codice sorgente completo lo trovi su Github.




Commenti
* Obbligatorio