This article discusses how to create applications with the gRPC framework and Quarkus. We will reuse the sample Service definition from first Java gRPC application and run it as Quarkus REST application.
Defining the gRPC Service
Firstly, we recommend reading this article for an introduction to the gRPC framework: Getting started with gRPC on Java
We will be using the FileManager Service definition from our Java example which returns a list of Files available in a remote directory:
syntax = "proto3"; option java_multiple_files = true; option java_package = "com.mastertheboss.filesystem"; option objc_class_prefix = "HLW"; package filesystem; service FileManager { rpc ReadDir (Directory) returns (FileList) {} } message Directory { string name = 1; } message FileList { string list = 1; }
Next, we will show how to run the above Service with Quarkus. There are two main benefits that Quarkus can bring to gRPC applications:
- Quarkus can register your Services and Start a plaintext or SSL gRPC automatically
- You can generate the the Java files from proto files with quarkus-maven-plugin
Creating the Quarkus project
To build an equivalent Quarkus application from our Proto file we will need to include the quarkus-grpc dependencies in a project. We also add the resteasy reactive dependencies to expose the gRPC with a REST Service:
<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-resteasy</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-resteasy-mutiny</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-grpc</artifactId> </dependency>
Next, copy the above proto file in the folder src/main/proto as example.proto
The project set up is complete, Next, we will build the REST Endpoint to test our Service:
package io.grpc.example.filesystem; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.QueryParam; import com.mastertheboss.filesystem.*; import io.quarkus.grpc.GrpcClient; import io.smallrye.mutiny.Uni; @Path("/filesystem") public class DemoGRPCEndpoint { @GrpcClient("filesystem") FileManagerGrpc.FileManagerBlockingStub blockingService; @GrpcClient("filesystem") FileManager service; @GET @Path("/blocking") public String helloBlocking(@QueryParam("dir") String dir) { FileList reply = blockingService.readDir(Directory.newBuilder().setName(dir).build()); return reply.getList(); } @GET @Path("/mutiny") public Uni<String> helloMutiny(@QueryParam("dir") String dir) { return service.readDir(Directory.newBuilder().setName(dir).build()) .onItem().transform((reply) -> reply.getList()); } }
The most interesting part, is the @GrpcClient which is used to inject gRPC stubs in the Endpoint.
The first one, inject a blocking stub using the gRPC API
@GrpcClient("hello") FileManagerGrpc.FileManagerBlockingStub blockingHelloService;
The second one, injects a service interface using the Mutiny API which represents streams receiving either an item or a failure (Uni)
@GrpcClient("hello") FileManager helloService;
Coding the Service Implementation
So far we have exposed the gRPC stubs via REST API. We still need to provide the Service implementation for the method readDir. The following DemoGRPCService implements the method as part of the FileManager interface:
package io.grpc.example.filesystem; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import io.grpc.stub.StreamObserver; import io.quarkus.grpc.GrpcService; import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.Uni; import com.mastertheboss.filesystem.*; @GrpcService public class DemoGRPCService implements FileManager { @Override public Uni<FileList> readDir(Directory req) { File f = new File(req.getName()); if (!f.isDirectory()) { throw new RuntimeException(req.getName() + " is not a directory."); } String name = req.getName(); String[] pathnames = f.list(); StringBuffer sb = new StringBuffer(); // For each pathname in the pathnames array for (String pathname : pathnames) { // Print the names of files and directories sb.append("[File=" +pathname+"]"); } return Uni.createFrom().item(sb.toString()) .map(res -> FileList.newBuilder().setList(res).build()); } }
Generating the Classes from Proto files
To generate the Classes and interfaces defined in the Proto file you include the plugin quarkus-maven-plugin with the goals generate-code and generate-code-tests:
<plugin> <groupId>${quarkus.platform.group-id}</groupId> <artifactId>quarkus-maven-plugin</artifactId> <version>${quarkus.platform.version}</version> <executions> <execution> <goals> <goal>build</goal> <goal>generate-code</goal> <goal>generate-code-tests</goal> </goals> </execution> </executions> </plugin>
Then, when you build the project:
mvn install
the Java classes will be generated under the folder target/generated-sources/grpc:
target/generated-sources/grpc └── com └── mastertheboss └── filesystem ├── Directory.java ├── DirectoryOrBuilder.java ├── FileList.java ├── FileListOrBuilder.java ├── FileManagerBean.java ├── FileManagerClient.java ├── FileManagerGrpc.java ├── FileManager.java ├── Helloworld.java └── MutinyFileManagerGrpc.java
Testing the application
You can use the development profile to test the application
mvn quarkus:dev
As you can see from the Console log, the gRPC Server started on 0.0.0.0:9000:
If needed, you can change the default port (9000) with the following configuration property:
quarkus.grpc.clients."client-name".port
We can test the gRPC Endpoints as follows:
$ curl localhost:8080/filesystem/blocking?dir=/tmp $ curl localhost:8080/filesystem/mutiny?dir=/tmp
In both cases, you should be able to see the directory listing for the server path “/tmp”.
You can also test the application through the native gRPC port. This requires insalling the grpcurl tool from https://github.com/fullstorydev/grpcurl/releases
Once installed, you can invoke the remote service as follows:
grpcurl --plaintext -d '{"name": "/tmp"}' localhost:9000 filesystem.FileManager.ReadDir
Conclusion
This article showed how to run a simple gRPC Service that we have designed in Java as Quarkus REST Service.
You can find the source code for this article here: https://github.com/fmarchioni/mastertheboss/tree/master/quarkus/grpc-demo