Introdução ao gRPC

Introdução ao gRPC

1. Introdução

gRPC is a high performance, open source RPC framework initially developed by Google. Ajuda a eliminar o código clichê e ajuda a conectar serviços poliglotas em e entre data centers.

2. Visão geral

A estrutura é baseada em um modelo cliente-servidor de chamadas de procedimento remoto. A client application can directly call methods on a server application as if it was a local object.

Este artigo seguirá as etapas a seguir para criar um aplicativo cliente-servidor típico usando o gRPC:

  1. Defina um serviço em um arquivo.proto

  2. Gere código de servidor e cliente usando o compilador de buffer de protocolo

  3. Crie o aplicativo do servidor, implementando as interfaces de serviço geradas e gerando o servidor gRPC

  4. Crie o aplicativo cliente, fazendo chamadas RPC usando stubs gerados

Vamos definir umHelloService simples que retorna saudações em troca do nome e do sobrenome.

3. Dependências do Maven

Vamos adicionar as dependências degrpc-netty,grpc-protobufegrpc-stub:


    io.grpc
    grpc-netty
    1.16.1


    io.grpc
    grpc-protobuf
    1.16.1


    io.grpc
    grpc-stub
    1.16.1

4. Definindo o serviço

Começamos definindo um serviço,specifying methods that can be called remotely along with their parameters and return types.

Isso é feito no arquivo.proto usandoprotocol buffers. Eles também são usados ​​para descrever a estrutura das mensagens de carga útil.

4.1. Configurações Básicas

Vamos criar um arquivoHelloService.proto para nossa amostraHelloService. Começamos adicionando alguns detalhes básicos de configuração:

syntax = "proto3";
option java_multiple_files = true;
package org.example.grpc;

A primeira linha informa ao compilador qual sintaxe é usada neste arquivo. Por padrão, o compilador gera todo o código Java em um único arquivo Java. A segunda linha substitui essa configuração e tudo será gerado em arquivos individuais.

Finalmente, especificamos o pacote que queremos usar para nossas classes Java geradas.

4.2. Definindo a Estrutura da Mensagem

Em seguida, definimos a mensagem:

message HelloRequest {
    string firstName = 1;
    string lastName = 2;
}

Isso define a carga útil da solicitação. Aqui, cada atributo que entra na mensagem é definido junto com seu tipo.

Um número exclusivo precisa ser atribuído a cada atributo, chamado como a tag. This tag is used by the protocol buffer to represent the attribute instead of using the attribute name.

Portanto, ao contrário do JSON, em que passaríamos o nome do atributofirstName todas as vezes, o buffer de protocolo usaria o número 1 para representarfirstName. A definição de carga útil da resposta é semelhante à solicitação.

Observe que podemos usar a mesma tag em vários tipos de mensagens:

message HelloResponse {
    string greeting = 1;
}

4.3. Definindo o Contrato de Serviço

Finalmente, vamos definir o contrato de serviço. Para o nossoHelloService, definimos uma operaçãohello():

service HelloService {
    rpc hello(HelloRequest) returns (HelloResponse);
}

A operaçãohello() aceita uma solicitação unária e retorna uma resposta unária. gRPC também oferece suporte a streaming prefixando a palavra-chavestream para a solicitação e resposta.

5. Gerando o Código

Agora, passamos o arquivoHelloService.proto para o compilador de buffer de protocoloprotoc para gerar os arquivos Java. Existem várias maneiras de desencadear isso.

5.1. Usando o compilador de buffer de protocolo

Download the compilere siga as instruções no arquivo README.

Você pode usar o seguinte comando para gerar o código:

protoc -I=$SRC_DIR --java_out=$DST_DIR $SRC_DIR/HelloService.proto

5.2. Usando o plugin Maven

Como desenvolvedor, você deseja que a geração de código seja totalmente integrada ao seu sistema de compilação. gRPC fornece umprotobuf-maven-plugin para o sistema de compilação Maven:


  
    
      kr.motd.maven
      os-maven-plugin
      1.6.1
    
  
  
    
      org.xolstice.maven.plugins
      protobuf-maven-plugin
      0.6.1
      
        
          com.google.protobuf:protoc:3.3.0:exe:${os.detected.classifier}
        
        grpc-java
        
          io.grpc:protoc-gen-grpc-java:1.4.0:exe:${os.detected.classifier}
        
      
      
        
          
            compile
            compile-custom
          
        
      
    
  

A extensão / pluginos-maven-plugin gera várias propriedades úteis de projeto dependentes da plataforma, como$\{os.detected.classifier}

6. Criação do servidor

Independentemente do método usado para geração de código, os seguintes arquivos-chave serão gerados:

  • HelloRequest.java – contém a definição de tipoHelloRequest

  • HelloResponse.java *–* contém a definição de tipoHelleResponse

  • HelloServiceImplBase.java *–* contém a classe abstrataHelloServiceImplBase que fornece uma implementação de todas as operações que definimos na interface de serviço

6.1. Substituindo a classe de base de serviço

Odefault implementation of the abstract class HelloServiceImplBase is to throw runtime exceptionio.grpc.StatusRuntimeException dizendo que o método não foi implementado.

Devemos estender essa classe e substituir o métodohello() mencionado em nossa definição de serviço:

public class HelloServiceImpl extends HelloServiceImplBase {

    @Override
    public void hello(
      HelloRequest request, StreamObserver responseObserver) {

        String greeting = new StringBuilder()
          .append("Hello, ")
          .append(request.getFirstName())
          .append(" ")
          .append(request.getLastName())
          .toString();

        HelloResponse response = HelloResponse.newBuilder()
          .setGreeting(greeting)
          .build();

        responseObserver.onNext(response);
        responseObserver.onCompleted();
    }
}

Se compararmos a assinatura dehello() com a que escrevemos no arquivoHellService.proto, notaremos que ela não retornaHelloResponse. Em vez disso, leva o segundo argumento comoStreamObserver<HelloResponse>, que é um observador de resposta, uma chamada de volta para o servidor chamar com sua resposta.

Desta forma,the client gets an option to make a blocking call or a non-blocking call.

O gRPC usa construtores para criar objetos. UsamosHelloResponse.newBuilder()e definimos o texto de saudação para construir um objetoHelloResponse. Definimos este objeto para o métodoonNext() do responseObserver para enviá-lo ao cliente.

Finalmente, precisamos chamaronCompleted() para especificar que terminamos de lidar com o RPC, caso contrário, a conexão será interrompida e o cliente apenas aguardará a chegada de mais informações.

6.2. Executando o Grpc Server

Em seguida, precisamos iniciar o servidor gRPC para escutar solicitações recebidas:

public class GrpcServer {
    public static void main(String[] args) {
        Server server = ServerBuilder
          .forPort(8080)
          .addService(new HelloServiceImpl()).build();

        server.start();
        server.awaitTermination();
    }
}

Aqui, novamente usamos o construtor para criar um servidor gRPC na porta 8080 e adicionar o serviçoHelloServiceImpl que definimos. start() iniciaria o servidor. Em nosso exemplo, chamaremosawaitTermination() para manter o servidor em execução em primeiro plano, bloqueando o prompt.

7. Criando o cliente

gRPC provides a channel construct which abstracts out the underlying details como conexão, pool de conexão, balanceamento de carga, etc.

Vamos criar um canal usandoManagedChannelBuilder. Aqui, especificamos o endereço e a porta do servidor.

Usaremos texto simples sem criptografia:

public class GrpcClient {
    public static void main(String[] args) {
        ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 8080)
          .usePlaintext()
          .build();

        HelloServiceGrpc.HelloServiceBlockingStub stub
          = HelloServiceGrpc.newBlockingStub(channel);

        HelloResponse helloResponse = stub.hello(HelloRequest.newBuilder()
          .setFirstName("example")
          .setLastName("gRPC")
          .build());

        channel.shutdown();
    }
}

Em seguida, precisamos criar um stub que usaremos para fazer a chamada remota real parahello(). The stub is the primary way for clients to interacts with the server. Ao usar stubs de geração automática, a classe stub terá construtores para agrupar o canal.

Aqui, estamos usando um stub de bloqueio / síncrono para que a chamada RPC aguarde a resposta do servidor e retorne uma resposta ou gere uma exceção. Existem outros dois tipos de stubs fornecidos pelo gRPC, que facilitam chamadas não-bloqueantes / assíncronas.

Finalmente, é hora de fazer a chamada RPChello(). Aqui, passamos oHelloRequest. Podemos usar os setters gerados automaticamente para definir os atributosfirstName,lastName do objetoHelloRequest.

Recebemos de volta o objetoHelloResponse retornado do servidor.

8. Conclusão

Neste tutorial, vimos como poderíamos usar o gRPC para facilitar o desenvolvimento da comunicação entre dois serviços, concentrando-se na definição do serviço e deixando o gRPC manipular todo o código padrão.

Como de costume, você encontrará as fontesover on GitHub.