Contents

Overview

The gRPC MicroProfile (MP) API is an extension to Helidon MP that enables building gRPC services integrated with MicroProfile APIs. Using Helidon MP simplifies the process of developing gRPC services compared to traditional approaches. Services can be implemented as plain POJOs, which are then automatically discovered and deployed at runtime—just like other Helidon MP web services.

Building gRPC services with Helidon gRPC MP is straightforward, allowing developers to focus on application logic rather than boilerplate code.

Maven Coordinates

To enable gRPC MicroProfile Server, add the following dependency to your project’s pom.xml (see Managing Dependencies).

<dependency>
    <groupId>io.helidon.microprofile.grpc</groupId>
    <artifactId>helidon-microprofile-grpc-server</artifactId>
</dependency>

Additional dependencies may be required depending on your application needs. See the gRPC MP Example for a complete example.

API

All Helidon gRPC MP annotations are defined in the Grpc interface. The following annotations are used to implement Helidon MP gRPC services:

  • @Grpc.GrpcService: an annotation that marks a class as a gRPC service.

  • @Grpc.GrpcMarshaller: an annotation on a type or method that specifies a named marshaller supplier. This annotation is required when not using Protobuf serialization.

  • @Grpc.Proto: an annotation for an optional method returning the Protobuf descriptor. For more information see the gRPC Reflection Service.

The following gRPC method types are supported:

  • @Grpc.Unary: a method that takes a single value (or void) and returns a single value (or void).

  • @Grpc.ServerStreaming: a method that takes a single value (or void) and returns a stream of values.

  • @Grpc.ClientStreaming: a method that takes a stream of values and returns a single value (or void).

  • @Grpc.Bidirectional: a method that takes a stream of values and returns a stream of values.

Usage

Defining a Service

The traditional approach to building Java gRPC services is to write Protobuf files describing a service, use these files to generate service stubs, and then implement the service methods from stub classes. In Helidon MP, you have a simpler option of just writing normal POJOs that refer to the Protobuf message types without the need to extend any generated stubs.

For example:

@ApplicationScoped
@Grpc.GrpcService
public class StringService {

    @Grpc.Unary("Upper")
    public Strings.StringMessage upper(Strings.StringMessage request) {
        String text = request.getText().toUpperCase();
        return Strings.StringMessage.newBuilder().setText(text).build();
    }
}

Note that the message types in Strings are generated from a Protobuf file, but the class itself is just a POJO that uses the Helidon MP annotations described above. In addition, @Grpc.Unary overrides the Java method name to match that in the Protobuf file, as shown next:

syntax = "proto3";

service StringService {
  rpc Upper (StringMessage) returns (StringMessage) {}
}

message StringMessage {
  string text = 1;
}

When using Maven, Protobuf files should be placed under the src/main/proto directory. It’s recommended to use the protobuf-maven-plugin to compile these files as part of the Maven build process. You can refer to the pom.xml file in the Helidon gRPC MP example for guidance.

Using Custom Marshalers

Even though it is recommended to use Protobuf message types, it is not mandatory. Traditional Java types can be used as long as custom marshalers are provided. For instance, in the example above we can use a String type instead of the generated type StringMessage, if we create a marshaler for it. For example,

@ApplicationScoped
@Grpc.GrpcService
@Grpc.GrpcMarshaller("string")
public class StringService {

    @Grpc.Unary("Upper")
    public String upper(String request) {
        return request.toLowerCase();
    }
}

In this example, the marshaler is provided using the name "string" and its supplier must be discoverable via CDI. The following is an example of a marshaler and its supplier for the String type:

@Dependent
@Named("string")
public class StringSupplier implements MarshallerSupplier {
    @Override
    public <T> MethodDescriptor.Marshaller<T> get(Class<T> clazz) {
        return new StringMarshaller<>();
    }
}

public class StringMarshaller<String>
        implements MethodDescriptor.Marshaller<String> {

    @Override
    public InputStream stream(String obj) {
        InputStream stream = null;
        // convert to stream
        return stream;
    }

    @Override
    @SuppressWarnings("unchecked")
    public String parse(InputStream in) {
        String string = null;
        // parse from stream
        return string;
    }

}

Annotating the supplier with @Dependent ensures discoverability provided CDI is configured to find all annotated beans in the corresponding beans.xml file.

Implementing a gRPC Extension

When unable to annotate a service class —for example when the code is built by a third party— another way to deploy non-CDI bean services is to implement a gRPC MP server extension. Such an extension will be called when the MP server is starting and be given the chance to provide additional services for deployment. An extension must implement the io.helidon.microprofile.grpc.server.spi.GrpcMpExtension interface.

For example, assuming that there is a gRPC service class called StringService that needs to be deployed, a gRPC server extension class might look like this:

public class MyExtension implements GrpcMpExtension {

    @Override
    public void configure(GrpcMpContext context) {           // (1)
        context.routing().service(new StringService());      // (2)
    }
}
  1. The configure method of the extension will be called to allow the extension to add extra configuration to the server.

  2. In this example, an instance of the StringService is registered with the routing, as described in the gRPC Server Routing documentation.

The GrpcMpExtension instances are discovered and loaded using the service loader, so for this example above to work, a file META-INF/services/io.helidon.microprofile.grpc.server.spi.GrpcMpExtension would need to be created with the name of the extension shown above.

gRPC Reflection Service

When a gRPC client interacts with a server, it needs to have access to the Protobuf file to learn about the available services, methods and message types in use. For many applications, this information is simply common knowledge between the two parties. However, in some cases, especially while developing a new service, it is convenient to use tools such as grpcurl or Postman to test a service.

Helidon includes a gRPC reflection service that can be queried by client tools to learn about the available services —similar to OpenAPI for REST services. The reflection service is implemented as a feature and can be enabled programmatically by adding the feature, or via config as follows:

  features:
    grpc-reflection:
      enabled: true

For more information see gRPC Server Configuration.

In Helidon MP, annotated services must provide access to the underlying Protobuf description to use the reflection service. Here is a modified version of StringService that adds an annotated method returning the descriptor:

@ApplicationScoped
@Grpc.GrpcService
public class StringService {

    @Grpc.Proto
    public Descriptors.FileDescriptor proto() {
        return Strings.getDescriptor();
    }

    @Grpc.Unary("Upper")
    public Strings.StringMessage upper(Strings.StringMessage request) {
        String text = request.getText().toUpperCase();
        return Strings.StringMessage.newBuilder().setText(text).build();
    }
}

A method annotated by @Grpc.Proto must return type Descriptors.FileDescriptor; such a descriptor is available from the Protobuf generated code, in this example, the Strings.getDescription() method. The reflection service shall call this method to provide service introspection to any clients that support the protocol.

Configuration

At the time of writing, there is no configuration that is specific to Helidon MP. For more information about gRPC configuration in SE, see gRPC Server Configuration.

Examples

Please refer to the Helidon gRPC MP Example.