gRPC Server
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:
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.