Projeto criado com o objetivo de colocar em prática estudo de gRPC em Java.
Nesse projeto existe um client e um server. Para simular uma chamada gRPC, é necessário utilizar o gradle para baixar as dependências e rodar a task generateProto
, que irá compilar o conteúdo existente no .proto file e transformar em classes para serem implementadas em build/generated
.
$ ./gradlew generateProto
Para rodar o server:
$ ./gradlew runServer
Para rodar o client que irá executar uma chamada teste:
$ ./gradlew runClient
Trata-se de um framework RPC (Remote Procedure Call - Requisição e Resposta) que permite manter comunicações em formato streaming bidirecional, utilizando o HTTP/2. Por padrão, utiliza Protocol Buffers para fazer a serialização dos dados.
REST | gRPC |
---|---|
Texto/JSON | Protocol Buffers |
unidirecional | bidirecional e assíncrono |
alta latência | baixa latência |
sem contrato | contrato definido (.proto) |
sem suporte a streaming | suporte a streaming |
design pré-definido | design é livre |
bibliotecas de terceiros | geração de código |
Para trabalhar com o protocol buffers é usado um arquivo .proto, que tem como objetivo definir as estruturas dos dados e seus respectivos serviços. A primeira coisa a ser informado num arquivo .proto é a sua versão, que costuma ser a 3.
syntax = "proto3";
A partir disso, é possível incluir algumas opções para a linguagem utilizada (Java, Go, etc...), como especificar pacotes, definir se vai ser gerado um ou vários arquivos, entre vários outros.
option java_package = "com.devfreitag";
option java_multiple_files=true;
O principal do arquivo são as message
e os service
, que definem qual vai ser o escopo a ser seguido.
Uma message
consiste em ser o contrato de um objeto, onde é definido quais suas propriedades especificando tipo, nome e posição.
message Object {
type propertyName = position;
}
Para utilizar as messages
em uma chamada gRPC, é possível definir uma interface no arquivo, onde a mesma terá seu código gerado pelo compilador do protocol buffer.
Por exemplo, se quisermos definir que teremos uma chamada gRPC que irá criar um novo Todo
, é possível fazer algo como:
syntax = "proto3";
message Todo {
string id = 1;
string description = 2;
bool done = 3;
}
message CreateTodoRequest {
string description = 1;
}
service TodoService {
rpc CreateTodo(CreateTodoRequest) returns (Todo) {}
}
Quando definimos quais as interfaces do service
que iremos implementar, pode ser especificado qual o tipo da requisição/resposta, sendo as opções:
-
Unary: uma chamada RPC normal, onde o cliente envia uma requisição para o servidor e aguarda uma resposta.
rpc Method(Request) returns (Response) {}
-
Server streaming: cliente envia uma única requisição para o servidor e recebe a resposta do servidor em formato streaming, no qual o cliente irá ler até não haver mais mensagens.
rpc Method(Request) returns (stream Response) {}
-
Client streaming: cliente envia uma sequência de mensagens para o servidor que, por sua vez, só vai processar e retornar a resposta quando o cliente terminar de enviar.
rpc Method(stream Request) returns (ResponseList) {}
-
Bidirectional streaming: tanto o cliente quanto o servidor vão enviar suas mensagens em formato streaming, de forma independente.
rpc Method(stream Request) returns (stream Response) {}
Abaixo, capturas de telas que demonstram de forma mais visual como é, na prática, feito as requisições gRPC. Foi utilizado o programa Postman como client.