The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.

Introdução ao gRPC

Esta página explica como começar a usar o gRPC na sua aplicação Quarkus. Embora esta página descreva como configurá-lo com o Maven, também é possível usar o Gradle.

Vamos imaginar que o você tenha um projeto Quarkus normal, gerado a partir do gerador de projetos Quarkus . A configuração padrão é suficiente, mas você também pode selecionar algumas extensões, se desejar.

Solução

Recomendamos que siga as instruções nas seções seguintes e crie a aplicação passo a passo. No entanto, você pode ir diretamente para o exemplo completo.

Clone o repositório Git: git clone https://github.com/quarkusio/quarkus-quickstarts.git, ou baixe um arquivo.

The solution is located in the grpc-plain-text-quickstart directory.

Configurando seu projeto

Edit the pom.xml file to add the Quarkus gRPC extension dependency (just under <dependencies>):

<dependency>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-grpc</artifactId>
</dependency>

Por padrão, a extensão quarkus-grpc se baseia no modelo de programação reativa. Neste guia, seguiremos uma abordagem reativa. Na seção dependencies do seu arquivo pom.xml, certifique-se de ter a dependência RESTEasy Reactive:

<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-resteasy-reactive</artifactId>
</dependency>

Make sure you have generate-code goal of quarkus-maven-plugin enabled in your pom.xml. If you wish to generate code from different proto files for tests, also add the generate-code-tests goal. Please note that no additional task/goal is required for the Gradle plugin.

<build>
    <plugins>
        <plugin>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-maven-plugin</artifactId>
            <version>${quarkus-plugin.version}</version>
            <extensions>true</extensions>
            <executions>
                <execution>
                    <goals>
                        <goal>build</goal>
                        <goal>generate-code</goal>
                        <goal>generate-code-tests</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

Com essa configuração, você pode colocar as definições do seu serviço e mensagens no diretório src/main/proto. O quarkus-maven-plugin irá gerar arquivos Java a partir dos seus arquivos proto.

O quarkus-maven-plugin recupera uma versão do protoc (o compilador protobuf) dos repositórios Maven. A versão recuperada corresponde ao seu sistema operacional e arquitetura de CPU. Se essa versão recuperada não funcionar no seu contexto, você pode forçar o uso de um classificador de sistema operacional diferente com -Dquarkus.grpc.protoc-os-classifier=seu-classificador-de-os (por exemplo, osx-x86_64). Você também pode baixar o binário adequado e especificar a localização via -Dquarkus.grpc.protoc-path=/caminho/para/protoc.

Alternatively to using the generate-code goal of the quarkus-maven-plugin, you can use protobuf-maven-plugin to generate these files, more in Generating Java files from proto with protobuf-maven-plugin

Vamos começar com um simples Hello service. Crie o arquivo src/main/proto/helloworld.proto com o seguinte conteúdo:

syntax = "proto3";

option java_multiple_files = true;
option java_package = "io.quarkus.example";
option java_outer_classname = "HelloWorldProto";

package helloworld;

// The greeting service definition.
service Greeter {
    // Sends a greeting
    rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
    string name = 1;
}

// The response message containing the greetings
message HelloReply {
    string message = 1;
}

Este arquivo proto define uma interface de serviço simples com um único método ( SayHello), e as mensagens trocadas ( HelloRequest contendo o nome HelloReply e contendo a mensagem de saudação).

Antes de começar a programar, precisamos gerar as classes usadas para implementar e consumir os serviços gRPC. Em um terminal, execute:

$ mvn compile

Uma vez gerado, pode consultar o diretório target/generated-sources/grpc:

target/generated-sources/grpc
└── io
    └── quarkus
        └── example
            ├── Greeter.java
            ├── GreeterBean.java
            ├── GreeterClient.java
            ├── GreeterGrpc.java
            ├── HelloReply.java
            ├── HelloReplyOrBuilder.java
            ├── HelloRequest.java
            ├── HelloRequestOrBuilder.java
            ├── HelloWorldProto.java
            └── MutinyGreeterGrpc.java

Estas são as classes que vamos utilizar.

proto files with imports

Protocol Buffers specification provides a way to import proto files. The Quarkus code generation mechanism lets you control the scope of dependencies to scan for possible imports by setting the quarkus.generate-code.grpc.scan-for-imports property to one of the following:

  • all - scan all the dependencies

  • none - don’t scan the dependencies, use only what is defined in the src/main/proto or src/test/proto

  • groupId1:artifactId1,groupId2:artifactId2 - scan only the dependencies with group id and artifact id in the list.

If not specified, the property is set to com.google.protobuf:protobuf-java. To override it, set the quarkus.generate-code.grpc.scan-for-imports property in your application.properties to the desired value, e.g.

quarkus.generate-code.grpc.scan-for-imports=all

proto files from dependencies

In some cases, you may want to use proto files from a different project to generate the gRPC stubs. In this case:

  1. Add a dependency on the artifact that contains the proto file to your project

  2. In application.properties, specify the dependencies you want to scan for proto files.

quarkus.generate-code.grpc.scan-for-proto=<groupId>:<artifactId>

The value of the property may be none, which is the default value, or a comma separated list of groupId:artifactId coordinates.

Diferentes implementações / tipos de gRPC

Outra coisa importante a se notar é que o suporte do Quarkus para gRPC atualmente inclui 3 diferentes tipos de uso do gRPC:

  1. implementação antiga do Vert.x gRPC com um servidor gRPC separado (padrão)

  2. nova implementação Vert.x gRPC em cima do servidor HTTP existente

  3. xDS gRPC wrapper sobre grpc-java com um servidor gRPC separado baseado no Netty

Documentações adicionais explicam como habilitar e usar cada um deles.

Implementação de um serviço gRPC

Agora que temos as classes geradas, vamos implementar o nosso serviço hello.

Com o Quarkus, implementar um serviço requer a implementação da interface de serviço gerada com base no Mutiny, uma API de Programação Reativa integrada no Quarkus, e expô-la como um bean CDI. Saiba mais sobre o Mutiny no [guia Mutiny](xref:mutiny-primer.adoc). A classe de serviço deve ser anotada com a anotação @io.quarkus.grpc.GrpcService.

Implementação de um serviço

Crie o arquivo src/main/java/org/acme/HelloService.java com o seguinte conteúdo:

package org.acme;

import io.quarkus.example.Greeter;
import io.quarkus.example.HelloReply;
import io.quarkus.example.HelloRequest;
import io.quarkus.grpc.GrpcService;
import io.smallrye.mutiny.Uni;

@GrpcService (1)
public class HelloService implements Greeter {  (2)

    @Override
    public Uni<HelloReply> sayHello(HelloRequest request) { (3)
        return Uni.createFrom().item(() ->
                HelloReply.newBuilder().setMessage("Hello " + request.getName()).build()
        );
    }
}
1 Exponha a sua implementação como um bean.
2 Implementar a interface de serviço gerada.
3 Implementar os métodos definidos na definição do serviço (neste caso, temos um único método).

Você também pode usar a API gRPC padrão em vez do Mutiny:

package org.acme;

import io.grpc.stub.StreamObserver;
import io.quarkus.example.GreeterGrpc;
import io.quarkus.example.HelloReply;
import io.quarkus.example.HelloRequest;
import io.quarkus.grpc.GrpcService;

@GrpcService (1)
public class HelloService extends GreeterGrpc.GreeterImplBase { (2)

    @Override
    public void sayHello(HelloRequest request, StreamObserver<HelloReply> responseObserver) { (3)
        String name = request.getName();
        String message = "Hello " + name;
        responseObserver.onNext(HelloReply.newBuilder().setMessage(message).build()); (4)
        responseObserver.onCompleted(); (5)
    }
}
1 Exponha a sua implementação como um bean.
2 Estende a classe ImplBase. Esta é uma classe gerada.
3 Implementar os métodos definidos na definição do serviço (neste caso, temos um único método).
4 Construir e enviar a resposta.
5 Fechar a resposta.
Se a lógica de implementação do seu serviço for bloqueante (usando E/S bloqueante, por exemplo), anote seu método com @Blocking. A anotação io.smallrye.common.annotation.Blocking instrui o framework a invocar o método anotado em uma thread de trabalho em vez da thread de E/S (event-loop).

O servidor gRPC

Os serviços são served by a server . Os serviços disponíveis (beans CDI ) são automaticamente registrados e expostos.

Por padrão, o servidor é exposto em localhost:9000 e utiliza texto simples (sem TLS) quando executado normalmente, e localhost:9001 para testes.

Executar a aplicação utilizando: mvn quarkus:dev.

Consumindo um serviço gRPC

Nesta seção, vamos consumir o serviço que expomos. Para simplificar, vamos consumir o serviço a partir da mesma aplicação, o que no mundo real não faz sentido.

Abra a classe org.acme.ExampleResource e edite o conteúdo para ficar:

package org.acme;

import io.quarkus.example.Greeter;
import io.quarkus.example.HelloRequest;
import io.quarkus.grpc.GrpcClient;
import io.smallrye.mutiny.Uni;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/hello")
public class ExampleResource {

    @GrpcClient                               (1)
    Greeter hello;                            (2)

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String hello() {
        return "hello";
    }

    @GET
    @Path("/{name}")
    public Uni<String> hello(String name) {
        return hello.sayHello(HelloRequest.newBuilder().setName(name).build())
                .onItem().transform(helloReply -> helloReply.getMessage());  (3)
    }
}
1 Injete o serviço e configure seu nome. O nome é utilizado na configuração da aplicação. Se não for especificado, é utilizado o nome do campo: hello neste caso específico.
2 Use a interface de serviço gerada com base na API Mutiny.
3 Invocar o serviço.

Precisamos configurar a aplicação para indicar onde o serviço hello é encontrado. No arquivo src/main/resources/application.properties , adicione a seguinte propriedade:

quarkus.grpc.clients.hello.host=localhost
  • hello é o nome utilizado na anotação @GrpcClient.

  • host configura o host do serviço (aqui é localhost).

Em seguida, abra http://localhost:8080/hello/quarkus em um navegador e deverá receber Hello quarkus!

Empacotando a aplicação

Like any other Quarkus applications, you can package it with: mvn package. You can also package the application into a native executable with: mvn package -Pnative.

Generating Java files from proto with protobuf-maven-plugin

Alternatively to using Quarkus code generation to generate stubs for proto files, you can also use protobuf-maven-plugin.

To do it, first define the 2 following properties in the <properties> section:

<grpc.version>1.53.0</grpc.version>
<protoc.version>3.22.0</protoc.version>

They configure the gRPC version and the protoc version.

Then, add to the build section the os-maven-plugin extension and the protobuf-maven-plugin configuration.

<build>
    <extensions>
        <extension>
            <groupId>kr.motd.maven</groupId>
            <artifactId>os-maven-plugin</artifactId>
            <version>${os-maven-plugin-version}</version>
        </extension>
    </extensions>

    <plugins>
        <plugin>
            <groupId>org.xolstice.maven.plugins</groupId>
            <artifactId>protobuf-maven-plugin</artifactId>      (1)
            <version>${protobuf-maven-plugin-version}</version>
            <configuration>
                <protocArtifact>com.google.protobuf:protoc:${protoc.version}:exe:${os.detected.classifier}</protocArtifact> (2)
                <pluginId>grpc-java</pluginId>
                <pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
                <protocPlugins>
                    <protocPlugin>
                        <id>quarkus-grpc-protoc-plugin</id>
                        <groupId>io.quarkus</groupId>
                        <artifactId>quarkus-grpc-protoc-plugin</artifactId>
                        <version>2.16.12.Final</version>
                        <mainClass>io.quarkus.grpc.protoc.plugin.MutinyGrpcGenerator</mainClass>
                    </protocPlugin>
                </protocPlugins>
            </configuration>
            <executions>
                <execution>
                    <id>compile</id>
                    <goals>
                        <goal>compile</goal>
                        <goal>compile-custom</goal>
                    </goals>
                </execution>
                <execution>
                    <id>test-compile</id>
                    <goals>
                        <goal>test-compile</goal>
                        <goal>test-compile-custom</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>

        <!-- ... -->
    </plugins>
</build>
1 The protobuf-maven-plugin that generates stub classes from your gRPC service definition (proto files).
2 The class generation uses a tool named protoc, which is OS-specific. That’s why we use the os-maven-plugin to target the executable compatible with the operating system.
This configuration instructs the protobuf-maven-plugin to generate the default gRPC classes and classes using Mutiny to fit with the Quarkus development experience.
When using protobuf-maven-plugin, instead of the quarkus-maven-plugin, every time you update the proto files, you need to re-generate the classes (using mvn compile).

gRPC classes from dependencies

When gRPC classes - the classes generated from proto files - are in a dependency of the application, then the dependency needs a Jandex index. The jandex-maven-plugin can be used to create a Jandex index. More information on this topic can be found in the Bean Discovery section of the CDI guide.

<build>
    <plugins>
        <plugin>
            <groupId>io.smallrye</groupId>
            <artifactId>jandex-maven-plugin</artifactId>
            <version>3.0.5</version>
            <executions>
                <execution>
                <id>make-index</id>
                <goals>
                    <goal>jandex</goal>
                </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>