Compila un servicio de gRPC con Java

1. Descripción general

gRPC es un conjunto de herramientas y marco de trabajo de llamada de procedimiento remoto (RPC) independiente del lenguaje y de la plataforma desarrollados en Google. Te permite definir un servicio mediante búferes de protocolo, un lenguaje y un conjunto de herramientas de serialización binario potentes. Luego, te permite generar stubs de cliente y servidor idiomáticos a partir de la definición de tu servicio en una variedad de lenguajes.

En este codelab, aprenderás a compilar un servicio basado en Java que exponga una API con el framework de gRPC y, luego, escribirás un cliente para usar el stub generado del cliente de gRPC.

Qué aprenderás

  • El lenguaje del búfer de protocolo
  • Cómo implementar un servicio de gRPC con Java
  • Cómo implementar un cliente de gRPC con Java

¿Cómo usarás este instructivo?

Ler Leer y completar los ejercicios

¿Cómo calificarías tu experiencia con la compilación de apps de Node.js?

Principiante Intermedio Avanzado

¿Cómo calificarías tu experiencia con la compilación de apps de Go?

Principiante Intermedio Avanzado .
.

2. Configuración y requisitos

Configuración del entorno de autoaprendizaje

  1. Accede a la consola de Cloud y crea un proyecto nuevo o reutiliza uno existente. Si aún no tienes una cuenta de Gmail o de Google Workspace, debes crear una.

96a9c957bc475304.png

b9a10ebdf5b5a448.png

a1e3c01a38fa61c2.png

Recuerde el ID de proyecto, un nombre único en todos los proyectos de Google Cloud (el nombre anterior ya se encuentra en uso y no lo podrá usar). Se mencionará más adelante en este codelab como PROJECT_ID.

  1. A continuación, deberás habilitar la facturación en la consola de Cloud para usar los recursos de Google Cloud recursos.

Ejecutar este codelab no debería costar mucho, tal vez nada. Asegúrate de seguir las instrucciones de la sección “Realiza una limpieza” en la que se aconseja cómo cerrar recursos para no incurrir en facturación más allá de este instructivo. Los usuarios nuevos de Google Cloud son aptos para participar en el programa Prueba gratuita de$300.

Google Cloud Shell

Si bien este codelab se puede operar desde tu computadora, en él usaremos Google Cloud Shell, un entorno de línea de comandos que se ejecuta en la nube.

Esta máquina virtual basada en Debian está cargada con todas las herramientas de desarrollo que necesitarás. Ofrece un directorio principal persistente de 5 GB y se ejecuta en Google Cloud, lo que permite mejorar considerablemente el rendimiento de la red y la autenticación. Esto significa que todo lo que necesitarás para este Codelab es un navegador (sí, funciona en una Chromebook).

  1. Para activar Cloud Shell desde la consola de Cloud, solo haz clic en Activar Cloud Shella8460e837e9f5fda.png (el aprovisionamiento y la conexión al entorno debería llevar solo unos minutos).

b532b2f19ab85dda.png

Captura de pantalla del 14 de junio de 2017 a las 10.13.43 p.m. .png

Una vez conectado a Cloud Shell, debería ver que ya se autenticó y que el proyecto ya se configuró con tu PROJECT_ID:

gcloud auth list

Resultado del comando

Credentialed accounts:
 - <myaccount>@<mydomain>.com (active)
gcloud config list project

Resultado del comando

[core]
project = <PROJECT_ID>

Si, por algún motivo, el proyecto no está configurado, solo emite el siguiente comando:

gcloud config set project <PROJECT_ID>

Si no conoce su PROJECT_ID, Observa el ID que usaste en los pasos de configuración o búscalo en el panel de la consola de Cloud:

2485e00c1223af09.png

Cloud Shell también configura algunas variables de entorno de forma predeterminada, lo que puede resultar útil cuando ejecutas comandos futuros.

echo $GOOGLE_CLOUD_PROJECT

Resultado del comando

<PROJECT_ID>
  1. Establece la zona predeterminada y la configuración del proyecto.
gcloud config set compute/zone us-central1-f

Puedes elegir una variedad de zonas diferentes. Para obtener más información, consulta Regiones y zonas.

3. Crea un Service de gRPC

Crea un nuevo proyecto de Java con Maven:

$ mvn archetype:generate -DgroupId=com.example.grpc \
 -DartifactId=grpc-hello-server \
 -DarchetypeArtifactId=maven-archetype-quickstart \
 -DinteractiveMode=false
$ cd grpc-hello-server

Agrega un archivo de definición de gRPC

En gRPC, las cargas útiles del servicio (solicitud y respuesta) y las operaciones del servicio deben capturarse en un IDL (lenguaje de definición de la interfaz). gRPC usa la sintaxis de Protobuffer 3 para definir cargas útiles y operaciones de mensajes. Creemos un archivo .proto para un servicio de saludo simple con una solicitud “Hello” y una respuesta “Hello”.

Primero, crea un directorio proto nuevo que contenga el archivo proto nuevo:

$ mkdir -p src/main/proto

Luego, crea un nuevo archivo proto src/main/proto/GreetingService.proto.

Puedes usar vim,nano, o emacs para editar el archivo:

src/main/proto/GreetingService.proto

syntax = "proto3";
package com.example.grpc;

// Request payload
message HelloRequest {
  // Each message attribute is strongly typed.
  // You also must assign a "tag" number.
  // Each tag number is unique within the message.
  string name = 1;

  // This defines a strongly typed list of String
  repeated string hobbies = 2;

  // There are many more basics types, like Enum, Map
  // See https://developers.google.com/protocol-buffers/docs/proto3
  // for more information.
}

message HelloResponse {
  string greeting = 1;
}

// Defining a Service, a Service can have multiple RPC operations
service GreetingService {
  // Define a RPC operation
  rpc greeting(HelloRequest) returns (HelloResponse);
}

Agrega dependencias y complementos de gRPC

Una vez que tengas la definición, podremos generar el stub del servidor y el stub del cliente a partir de este archivo. Deberás agregar las dependencias y los complementos de gRPC.

Primero, agrega las dependencias de gRPC a pom.xml:

pom.xml

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

Luego, agrega el complemento:

pom.xml

<project>
  ...
  <dependencies>
    ...
  </dependencies>
  <build>
    <extensions>
      <extension>
        <groupId>kr.motd.maven</groupId>
        <artifactId>os-maven-plugin</artifactId>
        <version>1.6.2</version>
      </extension>
    </extensions>
    <plugins>
      <plugin>
        <groupId>org.xolstice.maven.plugins</groupId>
        <artifactId>protobuf-maven-plugin</artifactId>
        <version>0.6.1</version>
        <configuration>
          <protocArtifact>com.google.protobuf:protoc:3.9.0:exe:${os.detected.classifier}</protocArtifact>
          <pluginId>grpc-java</pluginId>
          <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.24.0:exe:${os.detected.classifier}</pluginArtifact>
        </configuration>
        <executions>
          <execution>
            <goals>
              <goal>compile</goal>
              <goal>compile-custom</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>

</project>

Cómo generar los stubs

Cuando compiles la aplicación, el complemento convertirá las definiciones de proto en código Java.

$ mvn -DskipTests package

Para ver los archivos generados, haz lo siguiente:

$ find target/generated-sources

Implementar el servicio

Primero, crea una nueva clase GreetingServiceImpl que implemente la operación greeting:

src/main/java/com/example/grpc/GreetingServiceImpl.java

package com.example.grpc;

import io.grpc.stub.StreamObserver;

public class GreetingServiceImpl extends GreetingServiceGrpc.GreetingServiceImplBase {
  @Override
  public void greeting(GreetingServiceOuterClass.HelloRequest request,
        StreamObserver<GreetingServiceOuterClass.HelloResponse> responseObserver) {
  // HelloRequest has toString auto-generated.
    System.out.println(request);

    // You must use a builder to construct a new Protobuffer object
    GreetingServiceOuterClass.HelloResponse response = GreetingServiceOuterClass.HelloResponse.newBuilder()
      .setGreeting("Hello there, " + request.getName())
      .build();

    // Use responseObserver to send a single response back
    responseObserver.onNext(response);

    // When you are done, you must call onCompleted.
    responseObserver.onCompleted();
  }
}

Implementa el servidor

Por último, deberás iniciar un servidor para escuchar en un puerto y registrar esta implementación de servicio. Edita la clase App y su método principal:

src/main/java/com/example/grpc/App.java

package com.example.grpc;

import io.grpc.*;

public class App
{
    public static void main( String[] args ) throws Exception
    {
      // Create a new server to listen on port 8080
      Server server = ServerBuilder.forPort(8080)
        .addService(new GreetingServiceImpl())
        .build();

      // Start the server
      server.start();

      // Server threads are running in the background.
      System.out.println("Server started");
      // Don't exit the main thread. Wait until server is terminated.
      server.awaitTermination();
    }
}

Por último, ejecuta el servidor:

$ mvn -DskipTests package exec:java -Dexec.mainClass=com.example.grpc.App
...
Server Started

4. Consumo del Servicio

El generador ya generó todos los stubs del cliente. Para simplificar el lab, usaremos el mismo proyecto de Maven, pero solo agregaremos una nueva clase Client con un método principal nuevo.

Primero, haz clic en + para abrir una nueva sesión de Cloud Shell de modo que no necesites finalizar el servidor:

1ff0fda960b9adfc.png

En la sesión nueva, cambia al directorio grpc-hello-server:

$ cd grpc-hello-server

Luego, agrega la clase Client nueva:

src/main/java/com/example/grpc/Client.java

package com.example.grpc;

import io.grpc.*;

public class Client
{
    public static void main( String[] args ) throws Exception
    {
      // Channel is the abstraction to connect to a service endpoint
      // Let's use plaintext communication because we don't have certs
      final ManagedChannel channel = ManagedChannelBuilder.forTarget("localhost:8080")
        .usePlaintext(true)
        .build();

      // It is up to the client to determine whether to block the call
      // Here we create a blocking stub, but an async stub,
      // or an async stub with Future are always possible.
      GreetingServiceGrpc.GreetingServiceBlockingStub stub = GreetingServiceGrpc.newBlockingStub(channel);
      GreetingServiceOuterClass.HelloRequest request =
        GreetingServiceOuterClass.HelloRequest.newBuilder()
          .setName("Ray")
          .build();

      // Finally, make the call using the stub
      GreetingServiceOuterClass.HelloResponse response = 
        stub.greeting(request);

      System.out.println(response);

      // A Channel should be shutdown before stopping the process.
      channel.shutdownNow();
    }
}

Por último, ejecuta el cliente:

$ mvn -DskipTests package exec:java -Dexec.mainClass=com.example.grpc.Client
...
greeting: "Hello there, Ray"

Eso es todo. Es fácil, ¿verdad?

5. Servicio de transmisión

Hay mucho más que puedes probar. Por ejemplo, puedes compilar un servicio de transmisión con facilidad agregando la palabra clave stream en el archivo proto a la solicitud o al parámetro de respuesta, p.ej.,

src/main/proto/GreetingService.proto

syntax = "proto3";
package com.example.grpc;

...

// Defining a Service, a Service can have multiple RPC operations
service GreetingService {
  // MODIFY HERE: Update the return to streaming return.
  rpc greeting(HelloRequest) returns (stream HelloResponse);
}

Actualiza tu servidor para que envíe varias respuestas en lugar de solo una. Para hacerlo, realiza varias llamadas a responseObserver.onNext(...):

src/main/java/com/example/grpc/GreetingServiceImpl.java

package com.example.grpc;

import io.grpc.stub.StreamObserver;

public class GreetingServiceImpl extends GreetingServiceGrpc.GreetingServiceImplBase {
  @Override
  public void greeting(GreetingServiceOuterClass.HelloRequest request,
        StreamObserver<GreetingServiceOuterClass.HelloResponse> responseObserver) {
 
    ...

    // Feel free to construct different responses if you'd like.
    responseObserver.onNext(response);
    responseObserver.onNext(response);
    responseObserver.onNext(response);

    // When you are done, you must call onCompleted.
    responseObserver.onCompleted();
  }
}

El cliente debe usar un stub asíncrono en lugar del stub de bloqueo. Actualiza el código del cliente y asegúrate de actualizar el tipo stub a GreetingServiceStub:

src/main/java/com/example/grpc/Client.java

package com.example.grpc;

import io.grpc.*;

// New import
import io.grpc.stub.*;

public class Client
{
    public static void main( String[] args ) throws Exception
    {
      final ManagedChannel channel = ManagedChannelBuilder.forTarget("localhost:8080")
        .usePlaintext(true)
        .build();

      // Replace the previous synchronous code with asynchronous code.
      // This time use an async stub:
       GreetingServiceGrpc.GreetingServiceStub stub = GreetingServiceGrpc.newStub(channel);

      // Construct a request
      GreetingServiceOuterClass.HelloRequest request =
        GreetingServiceOuterClass.HelloRequest.newBuilder()
          .setName("Ray")
          .build();

      // Make an Asynchronous call. Listen to responses w/ StreamObserver
      stub.greeting(request, new StreamObserver<GreetingServiceOuterClass.HelloResponse>() {
        public void onNext(GreetingServiceOuterClass.HelloResponse response) {
          System.out.println(response);
        }
        public void onError(Throwable t) {
        }
        public void onCompleted() {
          // Typically you'll shutdown the channel somewhere else.
          // But for the purpose of the lab, we are only making a single
          // request. We'll shutdown as soon as this request is done.
          channel.shutdownNow();
        }
      });
    }
}

Vuelve a compilar la aplicación:

$ mvn -DskipTests package

Reinicia el servidor y el cliente, cada uno en su propia sesión de Cloud Shell.

Para iniciar el servidor, sigue estos pasos:

$ mvn exec:java -Dexec.mainClass=com.example.grpc.App
...
Server Started

Para iniciar el cliente, haz lo siguiente:

$ mvn exec:java -Dexec.mainClass=com.example.grpc.Client
...
greeting: "Hello there, Ray"
greeting: "Hello there, Ray"
greeting: "Hello there, Ray"

6. ¡Felicitaciones!

Temas abordados:

  • El lenguaje del búfer de protocolo
  • Cómo implementar un servidor de gRPC con Java
  • Cómo implementar un cliente de gRPC con Java

Próximos pasos:

Envíanos tus comentarios

  • Tómate un momento para completar nuestra breve encuesta