Antes de falarmos sobre grpc, precisamos comentar sobre http/2, pois este está presente nesta implementação.
Logo em seguida uma breve introdução referente ao grpc e seu funcionamento. Ao final uma implementação client/server.
Projeto completo encontra-se aqui: https://github.com/fabriciolfj/grpc-example
HTTP/2
Os dados trafegados são binários, utilizando o GZIP, por padrão.
O cabeçalho é stateful, ou seja, guarda estado transacional. Exemplo: primeira requisição envia um conjunto de cabeçalhos, na segunda, envia apenas os alterados. (HPACK)
Server push: ao realizar um requisição, o servidor já envia todo o contéudo necessário para renderizar a pagina por exemplo.
Multiplexação: com uma conexão tcp aberta, a comunição com o servidor é paralela, ou seja, não espera a requisição anterior terminar para chamar outra, e as respostas vão chegando conforme ficarem prontas.
GRPC
Desenvolvido pela google, esta implementação tem como objetivo aprimorar a performance da comunicação http.
Podemos utilizar os tipos de contéudos mais conhecidos do mercado, como json ou xml, no entando recomenda-se o uso do proto, devido a sua alta performance para serialização/deserialização.
Camadas
-
Stub: client chama o servidor através de stubs.
- camada mais alta
- gerada a partir de arquivos IDL (interface definition language)
- arquivos possui extensão .proto
Transporte: camada mais baixa, utiliza protocolo http2
Formas de comunicação
Existem 4 formas de comunicação
unary: cliente e servidor (uma request, um response) O canal de comunicação se fecha, após enviar a resposta
server-streaming: fluxo de dados por parte do servidor (um request, recebe um fluxo de mensagens). Canal de comunicação do lado do servidor fica aberto, enquanto ouver eventos e serem emitidos.
client-streaming: fluxo de dados por parte do cliente (envia um fluxo de mensagens, espera um response)
bidirectional-streaming: fluxo de dados por parte de ambos (cliente e servidor, envia um fluxo de mensagens, espera um fluxo de mensagens).
Vamos adotar um unary neste projeto e recomenda-se o uso do grpc entre aplicações e não como rota de acesso a usuários.
Code
Client
Configurando o client, para se comunicar com o server
@Component
@Log4j2
public class GrpcClient {
@Value("${grpc.server.host:localhost}")
private String host;
@Value("${grpc.server.port:9090}")
private int port;
private ManagedChannel channel;
private PersonServiceBlockingStub personServiceBlockingStub;
public void start() {
channel = ManagedChannelBuilder.forAddress(host, port)
.usePlaintext()
.build();
personServiceBlockingStub = PersonServiceGrpc.newBlockingStub(channel);
log.info("gRPC client connected to {}:{}", host, port);
}
public void shutdown() throws InterruptedException {
channel.shutdown().awaitTermination(1, TimeUnit.SECONDS);
log.info("gRPC client disconnected successfully.");
}
public PersonServiceBlockingStub getSourceServiceStub() {
return this.personServiceBlockingStub;
}
}
Classe para iniciar a comunicação com o server.
@Component
@Log4j2
public class GrpcClientRunner implements CommandLineRunner {
@Autowired
private GrpcClient grpcClient;
@Override
public void run(String... args) throws Exception {
grpcClient.start();
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
try {
grpcClient.shutdown();
} catch (InterruptedException e) {
log.error("Client stoppd with error: {}", e.getMessage());
}
}));
}
}
Server
Classe responsável por configurar o server
@Component
@Log4j2
public class GrpcServer {
@Value("${grpc.server.port:9090}")
private int port;
private Server server;
private PersonService personService;
private ExceptionInterceptor exceptionInterceptor;
public GrpcServer(PersonService personService, ExceptionInterceptor exceptionInterceptor) {
this.personService = personService;
this.exceptionInterceptor = exceptionInterceptor;
}
public void start() throws IOException, InterruptedException {
log.info("gRPC server is starting on port: {}.", port);
server = ServerBuilder.forPort(port)
.addService(personService)
.intercept(exceptionInterceptor)
.build().start();
log.info("gRPC server started and listening on port: {}.", port);
log.info("Following service are available: ");
server.getServices().stream()
.forEach(s -> log.info("Service Name: {}", s.getServiceDescriptor().getName()));
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
log.info("Shutting down gRPC server.");
GrpcServer.this.stop();
log.info("gRPC server shut down successfully.");
}));
}
private void stop() {
if (server != null) {
server.shutdown();
}
}
public void block() throws InterruptedException {
if (server != null) {
server.awaitTermination();
}
}
}
Classe responsável por subir o server
@Component
@RequiredArgsConstructor
public class GrpcServerRunner implements CommandLineRunner {
private final GrpcServer grpcServer;
@Override
public void run(String... args) throws Exception {
grpcServer.start();
grpcServer.block();
}
}
Exemplo de uma chamada ao um service, que envia a mensagem ao grpc server, através do grpc client.
private final GrpcClient grpcClient;
public PersonResponseDTO create(final PersonRequestDTO requestDTO) {
var request = PersonMapper.toRequest(requestDTO);
log.info("Request create: {}", request.toString());
return PersonMapper.toResponse(grpcClient.getSourceServiceStub().create(request));
}
Top comments (0)