DEV Community

fabriciolfj
fabriciolfj

Posted on • Edited on

Grpc - Spring Boot

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

  1. Os dados trafegados são binários, utilizando o GZIP, por padrão.

  2. 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)

  3. Server push: ao realizar um requisição, o servidor já envia todo o contéudo necessário para renderizar a pagina por exemplo.

  4. 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

  1. 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
  2. Transporte: camada mais baixa, utiliza protocolo http2

Formas de comunicação

Existem 4 formas de comunicação

  1. unary: cliente e servidor (uma request, um response) O canal de comunicação se fecha, após enviar a resposta

  2. 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.

  3. client-streaming: fluxo de dados por parte do cliente (envia um fluxo de mensagens, espera um response)

  4. 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;
    }

}

Enter fullscreen mode Exit fullscreen mode

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());
            }
        }));
    }
}
Enter fullscreen mode Exit fullscreen mode

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();
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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();
    }
}
Enter fullscreen mode Exit fullscreen mode

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));
    }
Enter fullscreen mode Exit fullscreen mode

Top comments (0)