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:
-
Defina um serviço em um arquivo.proto
-
Gere código de servidor e cliente usando o compilador de buffer de protocolo
-
Crie o aplicativo do servidor, implementando as interfaces de serviço geradas e gerando o servidor gRPC
-
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.