Consuming a gRPC Service

gRPC services can be injected in your application code.

Consuming gRPC services requires the gRPC classes to be generated. Place your proto files in src/main/proto and run mvn compile.

Stubs and Injection

gRPC generation provides several stubs, providing different way to consume a service. Quarkus gRPC can inject:

  • blocking stubs

  • reactive stubs based on Mutiny

In addition, it also can inject the gRPC io.grpc.Channel, that lets you create other types of stubs.

  1. @Inject @GrpcService("hello-service")
  2. MutinyGreeterGrpc.MutinyGreeterStub mutiny;
  3. @Inject @GrpcService("hello-service")
  4. GreeterGrpc.GreeterBlockingStub blocking;
  5. @Inject @GrpcService("hello-service")
  6. Channel channel;

The stub class names are computed from the service name. For example, if you use Greeter as service name as in:

  1. service Greeter {
  2. rpc SayHello (HelloRequest) returns (HelloReply) {}
  3. }

The Mutiny stub name is: MutinyGreeterGrpc.MutinyGreeterStub The blocking stub name is: GreeterGrpc.GreeterBlockingStub

Client injection must be qualified using @GrpcService. This annotation indicates the configuration prefix used to configure the service. For example, if you set it to hello-service, configuring the host of the service is done using hello-service.host.

Examples

Using a blocking and mutiny stubs

  1. @Inject @GrpcService("hello") GreeterGrpc.GreeterBlockingStub blockingHelloService;
  2. @Inject @GrpcService("hello") MutinyGreeterGrpc.MutinyGreeterStub mutinyHelloService;
  3. @GET
  4. @Path("/blocking/{name}")
  5. public String helloBlocking(@PathParam("name") String name) {
  6. return blockingHelloService.sayHello(HelloRequest.newBuilder().setName(name).build()).getMessage();
  7. }
  8. @GET
  9. @Path("/mutiny/{name}")
  10. public Uni<String> helloMutiny(@PathParam("name") String name) {
  11. return mutinyHelloService.sayHello(HelloRequest.newBuilder().setName(name).build())
  12. .onItem().transform(HelloReply::getMessage);
  13. }

Note that in this example, the quarkus.grpc.clients.hello.host property must be set.

Handling streams

gRPC allows sending and receiving streams:

  1. service Streaming {
  2. rpc Source(Empty) returns (stream Item) {} // Returns a stream
  3. rpc Sink(stream Item) returns (Empty) {} // Reads a stream
  4. rpc Pipe(stream Item) returns (stream Item) {} // Reads a streams and return a streams
  5. }

Using the Mutiny stub, you can interact with these as follows:

  1. package io.quarkus.grpc.example.streaming;
  2. import io.grpc.examples.streaming.Empty;
  3. import io.grpc.examples.streaming.Item;
  4. import io.grpc.examples.streaming.MutinyStreamingGrpc;
  5. import io.quarkus.grpc.runtime.annotations.GrpcService;
  6. import io.smallrye.mutiny.Multi;
  7. import javax.inject.Inject;
  8. import javax.ws.rs.GET;
  9. import javax.ws.rs.Path;
  10. import javax.ws.rs.PathParam;
  11. import javax.ws.rs.Produces;
  12. import javax.ws.rs.core.MediaType;
  13. @Path("/streaming")
  14. @Produces(MediaType.APPLICATION_JSON)
  15. public class StreamingEndpoint {
  16. @Inject @GrpcService("streaming") MutinyStreamingGrpc.MutinyStreamingStub client;
  17. @GET
  18. public Multi<String> invokeSource() {
  19. // Retrieve a stream
  20. return client.source(Empty.newBuilder().build())
  21. .onItem().transform(Item::getValue);
  22. }
  23. @GET
  24. @Path("sink/{max}")
  25. public Uni<Void> invokeSink(@PathParam("max") int max) {
  26. // Send a stream and wait for completion
  27. Multi<Item> inputs = Multi.createFrom().range(0, max)
  28. .map(i -> Integer.toString(i))
  29. .map(i -> Item.newBuilder().setValue(i).build());
  30. return client.sink(inputs).onItem().ignore().andContinueWithNull();
  31. }
  32. @GET
  33. @Path("/{max}")
  34. public Multi<String> invokePipe(@PathParam("max") int max) {
  35. // Send a stream and retrieve a stream
  36. Multi<Item> inputs = Multi.createFrom().range(0, max)
  37. .map(i -> Integer.toString(i))
  38. .map(i -> Item.newBuilder().setValue(i).build());
  39. return client.pipe(inputs).onItem().transform(Item::getValue);
  40. }
  41. }

Client configuration

For each gRPC service you inject in your application, you can configure the following attributes:

About the Duration format

The format for durations uses the standard java.time.Duration format. You can learn more about it in the Duration#parse() javadoc.

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, PT is implicitly prepended to the value to obtain a standard java.time.Duration format.

The service-name is the name set in the @GrpcService.

Example of configuration

The 2 following examples uses hello as service name. Don’t forget to replace it with the name you used in in the @GrpcService annotation.

Enabling TLS

To enable TLS, use the following configuration:

  1. quarkus.grpc.clients.hello.host=localhost
  2. quarkus.grpc.clients.hello.ssl.trust-store=src/main/resources/tls/ca.pem
When SSL/TLS is configured, plain-text is automatically disabled.

TLS with Mutual Auth

To use TLS with mutual authentication, use the following configuration:

  1. quarkus.grpc.clients.hello.host=localhost
  2. quarkus.grpc.clients.hello.plain-text=false
  3. quarkus.grpc.clients.hello.ssl.certificate=src/main/resources/tls/client.pem
  4. quarkus.grpc.clients.hello.ssl.key=src/main/resources/tls/client.key
  5. quarkus.grpc.clients.hello.ssl.trust-store=src/main/resources/tls/ca.pem