Vert.x gRPC Protoc Plugin

The easiest way to start using vertx-grpc is to utilize its built-in code generator plugin. To do so, one must define the protocol in the protobuffer format as required by gRPC.

syntax = "proto3";

option java_multiple_files = true;
option java_package = "examples";
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;
}

This is a very simple example showing the single request, single response mode.

Compile the RPC definition

Using the definition above we need to compile it.

You can compile the proto file using the protoc compiler if you like or you can integrate it in your build.

If you’re using Apache Maven you need to add the plugin:

<plugin>
 <groupId>org.xolstice.maven.plugins</groupId>
 <artifactId>protobuf-maven-plugin</artifactId>
 <version>0.6.1</version>
 <configuration>
   <protocArtifact>com.google.protobuf:protoc:${protoc.version}:exe:${os.detected.classifier}</protocArtifact>
   <pluginId>grpc-java</pluginId>
   <pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
   <protocPlugins>
     <protocPlugin>
       <id>vertx-grpc-protoc-plugin2</id>
       <groupId>io.vertx</groupId>
       <artifactId>vertx-grpc-protoc-plugin2</artifactId>
       <version>${stack.version}</version>
       <mainClass>io.vertx.grpc.plugin.VertxGrpcGenerator</mainClass>
     </protocPlugin>
   </protocPlugins>
 </configuration>
 <executions>
   <execution>
     <id>compile</id>
     <configuration>
       <outputDirectory>${project.basedir}/src/main/java</outputDirectory>
       <clearOutputDirectory>false</clearOutputDirectory>
     </configuration>
     <goals>
       <goal>compile</goal>
       <goal>compile-custom</goal>
     </goals>
   </execution>
 </executions>
</plugin>

The ${os.detected.classifier} property is used to make the build OS independant, on OSX it is replaced by osx-x86_64 and so on. To use it you need to add the os-maven-plugin[https://github.com/trustin/os-maven-plugin] in the build section of your pom.xml:

<build>
 ...
 <extensions>
   <extension>
     <groupId>kr.motd.maven</groupId>
     <artifactId>os-maven-plugin</artifactId>
     <version>1.4.1.Final</version>
   </extension>
 </extensions>
 ...
</build>

This plugin will compile your proto files under src/main/proto and make them available to your project.

If you’re using Gradle you need to add the plugin:

...
apply plugin: 'com.google.protobuf'
...
buildscript {
 ...
 dependencies {
   // ASSUMES GRADLE 2.12 OR HIGHER. Use plugin version 0.7.5 with earlier gradle versions
   classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.0'
 }
}
...
protobuf {
 protoc {
   artifact = 'com.google.protobuf:protoc:3.2.0'
 }
 plugins {
   grpc {
     artifact = "io.grpc:protoc-gen-grpc-java:1.25.0"
   }
   vertx {
     artifact = "io.vertx:vertx-grpc-protoc-plugin2:${vertx.grpc.version}"
   }
 }
 generateProtoTasks {
   all()*.plugins {
     grpc
     vertx
   }
 }
}

This plugin will compile your proto files under build/generated/source/proto/main and make them available to your project.

Writing a service

The plugin generates vertx idiomatic Vert.x service stubs.

Each service comes in two flavors, you can override the method you like depending on the style.

Unary services can return a Vert.x Future:

VertxGreeterGrpcServer.GreeterApi stub = new VertxGreeterGrpcServer.GreeterApi() {
 @Override
 public Future<HelloReply> sayHello(HelloRequest request) {
   return Future.succeededFuture(HelloReply.newBuilder().setMessage("Hello " + request.getName()).build());
 }
};

or process a Vert.x Promise

VertxGreeterGrpcServer.GreeterApi stub = new VertxGreeterGrpcServer.GreeterApi() {
 @Override
 public void sayHello(HelloRequest request, Promise<HelloReply> response) {
   response.complete(HelloReply.newBuilder().setMessage("Hello " + request.getName()).build());
 }
};

In both case you need to bind the stub to an existing GrpcServer:

server.bindAll(stub);

Streaming requests

Streaming requests are implemented with Vert.x ReadStream:

VertxStreamingGrpcServer.StreamingApi stub = new VertxStreamingGrpcServer.StreamingApi() {
 @Override
 public void sink(ReadStream<Item> stream, Promise<Empty> response) {
   stream.handler(item -> {
     System.out.println("Process item " + item.getValue());
   });
   // Send response
   stream.endHandler(v -> response.complete(Empty.getDefaultInstance()));
 }
};

Streaming responses

Streaming responses are implemented with Vert.x streams and comes in two flavors.

You can return a Vert.x ReadStream and let the service send it for you:

VertxStreamingGrpcServer.StreamingApi stub = new VertxStreamingGrpcServer.StreamingApi() {
 @Override
 public ReadStream<Item> source(Empty request) {
   return streamOfItem();
 }
};

or you can process a WriteStream:

VertxStreamingGrpcServer.StreamingApi stub = new VertxStreamingGrpcServer.StreamingApi() {
 @Override
 public void source(Empty request, WriteStream<Item> response) {
   response.write(Item.newBuilder().setValue("value-1").build());
   response.end(Item.newBuilder().setValue("value-2").build());
 }
};

Writing a client

The plugin generates Vert.x service clients.

A client wraps a GrpcClient and provides Vert.x idiomatic API to interact with the service:

VertxGreeterGrpcClient client = new VertxGreeterGrpcClient(grpcClient, server);

Unary services returns a Vert.x Future

Future<HelloReply> response = client.sayHello(HelloRequest.newBuilder().setName("John").build());

response.onSuccess(result -> System.out.println("Service responded: " + ar.result().getMessage()));

response.onFailure(err -> System.out.println("Service failure: " + ar.cause().getMessage()));

Streaming requests

Streaming requests use a lambda passed a Vert.x WriteStream of messages sent to the service

Future<Empty> response = client.sink(stream -> {
 stream.write(Item.newBuilder().setValue("Value 1").build());
 stream.write(Item.newBuilder().setValue("Value 2").build());
 stream.end(Item.newBuilder().setValue("Value 3").build());
});

Streaming responses

Streaming responses get a Vert.x ReadStream of messages sent by the service

Future<ReadStream<Item>> response = client.source(Empty.getDefaultInstance());

response.onSuccess(stream -> stream
 .handler(item -> System.out.println("Item " + item.getValue()))
 .exceptionHandler(err -> System.out.println("Stream failed " + err.getMessage()));
 .endHandler(v -> System.out.println("Stream ended")));

response.onFailure(err -> System.out.println("Service failure: " + ar.cause().getMessage()));