Consuming a gRPC Service
Os clientes gRPC podem ser injectados no código da sua aplicação.
Consuming gRPC services requires the gRPC classes to be generated.
Place your proto files in src/main/proto and run mvn compile.
|
Stubs e Injeção
A geração do gRPC fornece vários stubs, oferecendo diferentes maneiras de consumir um serviço gRPC. Você pode injetar:
-
uma interface de serviço utilizando a API Mutiny,
-
um stub bloqueante utilizando a API gRPC,
-
um stub reativo baseado no Mutiny,
-
o
io.grpc.Channeldo gRPC, que permite criar outros tipos de stubs.
import io.quarkus.grpc.GrpcClient;
import hello.Greeter;
import hello.GreeterGrpc.GreeterBlockingStub;
import hello.MutinyGreeterGrpc.MutinyGreeterStub;
class MyBean {
// A service interface using the Mutiny API
@GrpcClient("helloService") (1)
Greeter greeter;
// A reactive stub based on Mutiny
@GrpcClient("helloService")
MutinyGreeterGrpc.MutinyGreeterStub mutiny;
// A blocking stub using the gRPC API
@GrpcClient
GreeterGrpc.GreeterBlockingStub helloService; (2)
@GrpcClient("hello-service")
Channel channel;
}
| 1 | O ponto de injeção do cliente gRPC deve ser anotado com o qualificador @GrpcClient. Este qualificador pode ser usado para especificar o nome que é utilizado para configurar o cliente gRPC subjacente. Por exemplo, se você defini-lo como hello-service, a configuração do host do serviço é feita usando o qualificador quarkus.grpc.clients. hello-service.host. |
| 2 | Se o nome não for especificado através do endereço GrpcClient#value(), então o nome do campo é usado em seu lugar, por exemplo, helloService neste caso específico. |
Os nomes das classes stub são derivados do nome do serviço usado em seu arquivo proto . Por exemplo, se você usar Greeter como nome de serviço, como no seguinte caso:
option java_package = "hello";
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
Nesse caso, o nome da interface do serviço é: hello.Greeter, o nome do stub Mutiny é: hello.MutinyGreeterGrpc.MutinyGreeterStub e o nome do stub bloqueante é: hello.GreeterGrpc.GreeterBlockingStub.
Exemplos
Interface de serviço
import io.quarkus.grpc.GrpcClient;
import io.smallrye.mutiny.Uni;
import hello.Greeter;
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;
@GET
@Path("/mutiny/{name}")
public Uni<String> helloMutiny(String name) {
return hello.sayHello(HelloRequest.newBuilder().setName(name).build())
.onItem().transform(HelloReply::getMessage);
}
}
| 1 | O nome do serviço é derivado do ponto de injeção - o nome do campo é usado. A propriedade quarkus.grpc.clients.hello.host deve ser definida. |
Stub Bloqueante
import io.quarkus.grpc.GrpcClient;
import hello.GreeterGrpc.GreeterBlockingStub;
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("hello") (1)
GreeterGrpc.GreeterBlockingStub blockingHelloService;
@GET
@Path("/blocking/{name}")
public String helloBlocking(String name) {
return blockingHelloService.sayHello(HelloRequest.newBuilder().setName(name).build()).getMessage();
}
}
| 1 | A propriedade quarkus.grpc.clients.hello.host deve ser configurada. |
Lidando com fluxos
O gRPC permite o envia e recebimento de fluxos:
service Streaming {
rpc Source(Empty) returns (stream Item) {} // Returns a stream
rpc Sink(stream Item) returns (Empty) {} // Reads a stream
rpc Pipe(stream Item) returns (stream Item) {} // Reads a streams and return a streams
}
Utilizando o stub Mutiny, você pode interagir com eles da seguinte forma:
package io.quarkus.grpc.example.streaming;
import io.grpc.examples.streaming.Empty;
import io.grpc.examples.streaming.Item;
import io.grpc.examples.streaming.MutinyStreamingGrpc;
import io.quarkus.grpc.GrpcClient;
import io.smallrye.mutiny.Multi;
import io.smallrye.mutiny.Uni;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
@Path("/streaming")
@Produces(MediaType.APPLICATION_JSON)
public class StreamingEndpoint {
@GrpcClient
MutinyStreamingGrpc.MutinyStreamingStub streaming;
@GET
public Multi<String> invokeSource() {
// Retrieve a stream
return streaming.source(Empty.newBuilder().build())
.onItem().transform(Item::getValue);
}
@GET
@Path("sink/{max}")
public Uni<Void> invokeSink(int max) {
// Send a stream and wait for completion
Multi<Item> inputs = Multi.createFrom().range(0, max)
.map(i -> Integer.toString(i))
.map(i -> Item.newBuilder().setValue(i).build());
return streaming.sink(inputs).onItem().ignore().andContinueWithNull();
}
@GET
@Path("/{max}")
public Multi<String> invokePipe(int max) {
// Send a stream and retrieve a stream
Multi<Item> inputs = Multi.createFrom().range(0, max)
.map(i -> Integer.toString(i))
.map(i -> Item.newBuilder().setValue(i).build());
return streaming.pipe(inputs).onItem().transform(Item::getValue);
}
}
Configuração do cliente
Para cada serviço gRPC que você injeta em sua aplicação, você pode configurar os seguintes atributos:
Configuration property fixed at build time - All other configuration properties are overridable at runtime
Tipo |
Padrão |
|
|---|---|---|
The gRPC service port. Environment variable: Show more |
int |
|
The host name / IP on which the service is exposed. Environment variable: Show more |
string |
required |
The classpath path or file path to a server certificate or certificate chain in PEM format. Environment variable: Show more |
path |
|
The classpath path or file path to the corresponding certificate private key file in PEM format. Environment variable: Show more |
path |
|
An optional trust store which holds the certificate information of the certificates to trust The trust store can be either on classpath or in an external file. Environment variable: Show more |
path |
|
Use a name resolver. Defaults to dns. If set to "stork", host will be treated as SmallRye Stork service name Environment variable: Show more |
string |
|
Whether Environment variable: Show more |
booleano |
|
The duration after which a keep alive ping is sent. Environment variable: Show more |
||
The flow control window in bytes. Default is 1MiB. Environment variable: Show more |
int |
|
The duration without ongoing RPCs before going to idle mode. Environment variable: Show more |
||
The amount of time the sender of a keep alive ping waits for an acknowledgement. Environment variable: Show more |
||
Whether keep-alive will be performed when there are no outstanding RPC on a connection. Environment variable: Show more |
booleano |
|
The max number of hedged attempts. Environment variable: Show more |
int |
|
The max number of retry attempts. Retry must be explicitly enabled. Environment variable: Show more |
int |
|
The maximum number of channel trace events to keep in the tracer for each channel or sub-channel. Environment variable: Show more |
int |
|
The maximum message size allowed for a single gRPC frame (in bytes). Default is 4 MiB. Environment variable: Show more |
int |
|
The maximum size of metadata allowed to be received (in bytes). Default is 8192B. Environment variable: Show more |
int |
|
The negotiation type for the HTTP/2 connection. Accepted values are: Environment variable: Show more |
string |
|
Overrides the authority used with TLS and HTTP virtual hosting. Environment variable: Show more |
string |
|
The per RPC buffer limit in bytes used for retry. Environment variable: Show more |
long |
|
Whether retry is enabled. Note that retry is disabled by default. Environment variable: Show more |
booleano |
|
The retry buffer size in bytes. Environment variable: Show more |
long |
|
Use a custom user-agent. Environment variable: Show more |
string |
|
Use a custom load balancing policy. Accepted values are: Environment variable: Show more |
string |
|
The compression to use for each call. The accepted values are Environment variable: Show more |
string |
|
The deadline used for each call.
The format uses the standard Environment variable: Show more |
|
About the Duration format
The format for durations uses the standard You can also provide duration values starting with a number.
In this case, if the value consists only of a number, the converter treats the value as seconds.
Otherwise, |
O client-name é o nome definido no @GrpcClient ou derivado do ponto de injeção, se não for explicitamente definido.
Os exemplos a seguir usam hello como o nome do cliente. Não se esqueça de substituí-lo pelo nome que você usou na anotação @GrpcClient .
Habilitando o TLS
Para habilitar o TLS, use a seguinte configuração. Lembre-se de que todos os caminhos na configuração podem especificar um recurso no classpath (geralmente em src/main/resources ou de sua subpasta) ou um arquivo externo.
quarkus.grpc.clients.hello.host=localhost
# either a path to a classpath resource or to a file:
quarkus.grpc.clients.hello.ssl.trust-store=tls/ca.pem
When SSL/TLS is configured, plain-text is automatically disabled.
|
TLS com autenticação mútua
Para utilizar o TLS com autenticação mútua, utilize a seguinte configuração:
quarkus.grpc.clients.hello.host=localhost
quarkus.grpc.clients.hello.plain-text=false
# all the following may use either a path to a classpath resource or to a file:
quarkus.grpc.clients.hello.ssl.certificate=tls/client.pem
quarkus.grpc.clients.hello.ssl.key=tls/client.key
quarkus.grpc.clients.hello.ssl.trust-store=tls/ca.pem
Client Deadlines
It’s always reasonable to set a deadline (timeout) for a gRPC client, i.e. to specify a duration of time after which the RPC times out and the client receives the status error DEADLINE_EXCEEDED.
You can specify the deadline via the quarkus.grpc.clients."service-name".deadline configuration property, e.g.:
quarkus.grpc.clients.hello.host=localhost
quarkus.grpc.clients.hello.deadline=2s (1)
| 1 | Set the deadline for all injected clients. |
Cabeçalhos gRPC
Da mesma forma que no HTTP, chamadas gRPC podem transportar cabeçalhos junto com a mensagem. Cabeçalhos podem ser úteis, por exemplo, para autenticação.
Para definir cabeçalhos para uma chamada gRPC, crie um cliente com cabeçalhos anexados e, em seguida, realize a chamada neste cliente:
import javax.enterprise.context.ApplicationScoped;
import examples.Greeter;
import examples.HelloReply;
import examples.HelloRequest;
import io.grpc.Metadata;
import io.quarkus.grpc.GrpcClient;
import io.quarkus.grpc.GrpcClientUtils;
import io.smallrye.mutiny.Uni;
@ApplicationScoped
public class MyService {
@GrpcClient
Greeter client;
public Uni<HelloReply> doTheCall() {
Metadata extraHeaders = new Metadata();
if (headers) {
extraHeaders.put("my-header", "my-interface-value");
}
Greeter alteredClient = GrpcClientUtils.attachHeaders(client, extraHeaders); (1)
return alteredClient.sayHello(HelloRequest.newBuilder().setName(name).build()); (2)
}
}
| 1 | Alterar o cliente para fazer chamadas com os extraHeaders anexados |
| 2 | Realize a chamada com o cliente alterado. O cliente original permanece inalterado. |
GrpcClientUtils funciona com todas as variações de clientes.
Interceptores de clientes
Um interceptor de cliente gRPC pode ser implementado por um bean CDI que também implementa a interface io.grpc.ClientInterceptor . Você pode anotar um cliente injetado com @io.quarkus.grpc.RegisterClientInterceptor para registrar o interceptor especificado para a instância específica do cliente. A anotação @RegisterClientInterceptor pode ser repetida. Como alternativa, se você deseja aplicar o interceptador a qualquer cliente injetado, anote o bean do interceptador com @io.quarkus.grpc.GlobalInterceptor .
import io.quarkus.grpc.GlobalInterceptor;
import io.grpc.ClientInterceptor;
@GlobalInterceptor (1)
@ApplicationScoped
public class MyInterceptor implements ClientInterceptor {
@Override
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(MethodDescriptor<ReqT, RespT> method,
CallOptions callOptions, Channel next) {
// ...
}
}
| 1 | Este interceptor é aplicado a todos os clientes gRPC injectados. |
| Check the ClientInterceptor JavaDoc to properly implement your interceptor. |
@RegisterClientInterceptor Exampleimport io.quarkus.grpc.GrpcClient;
import io.quarkus.grpc.RegisterClientInterceptor;
import hello.Greeter;
@ApplicationScoped
class MyBean {
@RegisterClientInterceptor(MySpecialInterceptor.class) (1)
@GrpcClient("helloService")
Greeter greeter;
}
| 1 | Regista o MySpecialInterceptor para este cliente específico. |
When you have multiple client interceptors, you can order them by implementing the javax.enterprise.inject.spi.Prioritized interface:
@ApplicationScoped
public class MyInterceptor implements ClientInterceptor, Prioritized {
@Override
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(MethodDescriptor<ReqT, RespT> method,
CallOptions callOptions, Channel next) {
// ...
}
@Override
public int getPriority() {
return 10;
}
}
Os interceptadores com a maior prioridade são chamados primeiro. A prioridade padrão, usada se o interceptador não implementar a interface Prioritized , é 0 .
Métricas de Cliente gRPC
Habilitando a coleta de métricas
gRPC client metrics are automatically enabled when the application also uses the quarkus-micrometer extension.
Micrometer collects the metrics of all the gRPC clients used by the application.
Como exemplo, se você exportar as métricas para o Prometheus, obterá:
# HELP grpc_client_responses_received_messages_total The total number of responses received
# TYPE grpc_client_responses_received_messages_total counter
grpc_client_responses_received_messages_total{method="SayHello",methodType="UNARY",service="helloworld.Greeter",} 6.0
# HELP grpc_client_requests_sent_messages_total The total number of requests sent
# TYPE grpc_client_requests_sent_messages_total counter
grpc_client_requests_sent_messages_total{method="SayHello",methodType="UNARY",service="helloworld.Greeter",} 6.0
# HELP grpc_client_processing_duration_seconds The total time taken for the client to complete the call, including network delay
# TYPE grpc_client_processing_duration_seconds summary
grpc_client_processing_duration_seconds_count{method="SayHello",methodType="UNARY",service="helloworld.Greeter",statusCode="OK",} 6.0
grpc_client_processing_duration_seconds_sum{method="SayHello",methodType="UNARY",service="helloworld.Greeter",statusCode="OK",} 0.167411625
# HELP grpc_client_processing_duration_seconds_max The total time taken for the client to complete the call, including network delay
# TYPE grpc_client_processing_duration_seconds_max gauge
grpc_client_processing_duration_seconds_max{method="SayHello",methodType="UNARY",service="helloworld.Greeter",statusCode="OK",} 0.136478028
O nome do serviço, o método e o tipo podem ser encontrados nas tags.
Modo de desenvolvimento
Por padrão, ao iniciar a aplicação no modo de desenvolvimento, um servidor gRPC é iniciado, mesmo que nenhum serviço esteja configurado. Você pode configurar o comportamento do modo de desenvolvimento da extensão gRPC usando as seguintes propriedades.
Configuration property fixed at build time - All other configuration properties are overridable at runtime
Tipo |
Padrão |
|
|---|---|---|
Start gRPC server in dev mode even if no gRPC services are implemented. By default set to Environment variable: Show more |
booleano |
|