Contents

Overview

The gRPC Microprofile APIs are an extension to Helidon MP to allow building of gRPC services and clients that integrate with the Microprofile APIs. Using Helidon gRPC MP makes building gRPC services and clients an easier process compared to the traditional approach using Protobuf files and code generation. Services can be built using POJOs that are then discovered and deployed at runtime in the same way the Helidon MP discovers and deploys web resources in the MP HTTP server.

Building gRPC services using Helidon gRPC MP is very simple and allows the developer to concentrate on their application logic without needing to write a lot of boilerplate gRPC 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>
Copied

API

The following annotations are used to implement Helidon MP gRPC Services:

  • @Grpc - an annotation used to mark a class as representing a gRPC service.

  • @GrpcMarshaller - an annotation used to annotate a type or method to specify the named marshaller supplier to use for rpc method calls.

gRPC Method Types:

  • @Unary - a simple method with at most a single request value and returning at most a single response value.

  • @ServerStreaming - A method that takes at most a single request value but may return zero or more response values.

  • @Client Streaming - A request that takes one or more request values and returns at most one response value.

  • @Bidirectional - A method that can take one or more request values and return zero or more response values.

Usage

Defining a Service

The traditional approach to building Java gRPC services is to write Protobuf files describing the service, use these files to generate service stubs, and then implement the service methods by extending the generated stub classes. Using Helidon gRPC MP, all you need to do is write an annotated service implementation class that is just a normal POJO.

For example:

Simple gRPC Service
@ApplicationScoped
@io.helidon.microprofile.grpc.core.Grpc
public class StringService {

    @io.helidon.microprofile.grpc.core.Unary
    public String upper(String s) {
        return s == null ? null : s.toUpperCase();
    }
}
Copied

The code above is a simple service with a single unary method that just converts a String to uppercase. The important parts in the example are the @ApplicationScoped, @Grpc and @Unary annotations. These, along with other annotations discussed later, allow the gRPC MP APIs to discover, configure and deploy the service.

Of course Helidon gRPC MP does not preclude you from using the Protobuf files approach as traditional gRPC Java services also work in a gRPC MP server.

As already shown above, a Helidon gRPC MP service is just an annotated POJO. To make a class a service, it requires two annotations.

@ApplicationScoped                             
@io.helidon.microprofile.grpc.core.Grpc        
public class StringService {
    /* code is omitted */
}
Copied
  • The ApplicationScoped annotation is what makes the service implementation a CDI bean and hence discoverable.
  • The Grpc annotation is what defines the class as a gRPC service so that when the bean is discovered, it is then deployed by the gRPC MP server.

Service Name

By default when a class is annotated with Grpc, the class name will be used as the gRPC service name. So in the example above, the service name will be StringService. This can be changed by supplying a name to the annotation.

@ApplicationScoped
@io.helidon.microprofile.grpc.core.Grpc(name="Strings") 
public class StringService {
Copied
  • The name of the deployed service will be Strings.

Defining Service Methods

Once a class is properly annotated to make it a gRPC MP service, it needs to have service methods that implement the application business logic. In gRPC there are four different types of method:

  • Unary - a simple method with at most a single request value and returning at most a single response value.

  • Server Streaming - a method that takes at most a single request value but may return zero or more response values.

  • Client Streaming - a request that takes one or more request values and returns at most one response value.

  • Bi-directional Streaming - a method that can take one or more request values and return zero or more response values.

The Helidon gRPC MP API determines a method type by its annotation, which should be one of the following:

@io.helidon.microprofile.grpc.core.Unary
@io.helidon.microprofile.grpc.core.ServerStreaming
@io.helidon.microprofile.grpc.core.ClientStreaming
@io.helidon.microprofile.grpc.core.Bidirectional
Copied

Request and Response Types

A gRPC service method typically takes a request parameter and returns a response value (streaming methods may take or return multiple requests or responses). In traditional gRPC Java, the types used for the request and response values must be Protobuf serializable classes but this is not the case with Helidon gRPC. Helidon supports pluggable Marshallers and by default will support Protobuf types. Any type that can be marshalled by the built-in marshallers or custom supplied marshaller may be used as a request or response type.

Unary Methods

A unary gRPC method is the simplest type of service method. Typically a unary method takes a request value and returns a response value, but this is not always the case. A unary method could just as easily take no request parameter and/or return no response.

All of the signatures below are valid unary methods in Helidon gRPC MP.

// A unary method with a simple request and response
@io.helidon.microprofile.grpc.core.Unary
public ResponseType invoke(RequestType req)

// A unary method that just returns a response
@io.helidon.microprofile.grpc.core.Unary
public ResponseType invoke()

// A unary method that takes a request but returns no response
@io.helidon.microprofile.grpc.core.Unary
public void invoke(RequestType req)

// A unary method that takes no request and returns no response
@io.helidon.microprofile.grpc.core.Unary
public void invoke()

// An async unary request that takes a request and returns a future
// that will complete when the response is ready
@io.helidon.microprofile.grpc.core.Unary
public CompletableFuture<ResponseType> invoke(RequestType req)

// An async unary request that takes no request and returns a future
// that will complete when the response is ready
@io.helidon.microprofile.grpc.core.Unary
public CompletableFuture<ResponseType> invoke()

// The standard gRPC Java unary method signature
@io.helidon.microprofile.grpc.core.Unary
public void invoke(RequestType req, StreamObserver<ResponseType> observer)

// The standard gRPC Java unary method signature but without a request type
@io.helidon.microprofile.grpc.core.Unary
public void invoke(StreamObserver<ResponseType> observer)

// A unary method that takes a request type and a future to complete
// with the response type
@io.helidon.microprofile.grpc.core.Unary
public void invoke(RequestType req, CompletableFuture<ResponseType> observer)

// A unary method that takes no request type but just takes a future
// to complete with the response type
@io.helidon.microprofile.grpc.core.Unary
public void invoke(CompletableFuture<ResponseType> observer)
Copied

The various signatures supported above allow the service developer to choose the method signature that best fits their application business logic without needing to worry about handling standard gRPC Java requests and StreamObservers. The standard gRPC Java method signature is in the list above so it can still be used if required.

Server Streaming Methods

A server streaming method receives a requests from the client and when the request stream is complete, it sends back a stream of response values. A traditional gRPC Java server streaming method takes two parameters, the request and a StreamObserver that is used to send back the single response in the same way that a unary method sends a response. As with unary methods, Helidon gRPC MP supports different method signatures for server streaming methods.

All of the signatures below are valid server streaming methods in Helidon gRPC MP.

// The standard gRPC Java server streaming method signature
@io.helidon.microprofile.grpc.core.ServerStreaming
public void invoke(RequestType req, StreamObserver<ResponseType> observer)

// A server streaming method that uses a Stream to send the responses to the client
@io.helidon.microprofile.grpc.core.ServerStreaming
public Stream<ResponseType> invoke(RequestType req)

// The server streaming method without a request parameter
@io.helidon.microprofile.grpc.core.ServerStreaming
public void invoke(StreamObserver<ResponseType> observer)

// A server streaming method without a request parameter
// that uses a Stream to send the responses to the client
@io.helidon.microprofile.grpc.core.ServerStreaming
public Stream<ResponseType> invoke(RequestType req)
Copied

As with unary methods, the Helidon gRPC MP API supports multiple different method signatures for implementing server streaming methods.

Client Streaming Methods

A client streaming method receives a stream of requests from the client and when the request stream is complete, it sends back a response. A traditional gRPC Java client streaming method takes two StreamObserver parameters, one is the stream of client requests and the other is used to send back the single response in the same way that a unary method sends a response. As with unary methods, Helidon gRPC MP supports different method signatures for client streaming methods.

All of the signatures below are valid client streaming methods in Helidon gRPC MP.

// The standard gRPC Java client streaming method signature
@io.helidon.microprofile.grpc.core.ClientStreaming
public StreamObserver<RequestType> invoke(StreamObserver<ResponseType> observer)

// The gRPC Java client streaming method with an asynchronous response
@io.helidon.microprofile.grpc.core.ClientStreaming
public StreamObserver<RequestType> invoke(CompletableFuture<ResponseType> observer)
Copied

Bi-Directional Streaming Methods

A bi-directional streaming method is a method that is a constant stream of client requests and server responses. Other than the standard gRPC Java StreamObserver, there are no other built-in types that make sense to use to implement different method signatures for a bidirectional method so the only supported signature is the standard gRPC Java method.

@io.helidon.microprofile.grpc.core.Bidirectional
public StreamObserver<RequestType> invoke(StreamObserver<ResponseType> observer)
Copied

Deploying Protobuf Services

The examples above show how simple it is to write gRPC services with basic POJOs. There may be cases, however, where there is a requirement to deploy services built the traditional way using gRPC Java generated classes or built as non-microprofile Helidon gRPC services.

Annotate the Service Implementation

When the gRPC MP server is starting, it will discover all CDI beans of type io.grpc.BindableService. Service sub-classes implemented the traditional way with code generation are instances of BindableService so by annotating the implementation class with the @ApplicationScoped annotation, they become discoverable and will be deployed into the gRPC server.

@ApplicationScoped
public class StringService
    extends StringServiceGrpc.StringServiceImplBase {
Copied

In exactly the same way, if a class is an implementation of io.helidon.grpc.server.GrpcService, then it will be discovered and deployed when the MP gRPC server starts by simply annotating the class with the @ApplicationScoped annotation.

@ApplicationScoped
public class StringService implements GrpcService {
Copied

Implement a GrpcMpExtension

If it is not possible to annotate the service class (for example the code is built by a third party), another way to deploy non-CDI bean services is to implement a gRPC MP server extension. The extension will then be called when the MP server is starting and be given the chance to add additional services for deployment. An extension should implement the io.helidon.microprofile.grpc.server.spi.GrpcMpExtension interface.

For example, assuming that there was a gRPC service class called StringService that needed to be deployed, an extension class might look like this:

public class MyExtension
        implements GrpcMpExtension {
    @Override
    public void configure(GrpcMpContext context) {  
        context.routing()
               .register(new ServiceService());     
    }
}
Copied
  • The configure method of the extension will be called to allow the extension to add extra configuration to the server.
  • 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 the example above to work, a file META-INF/services/io.helidon.microprofile.grpc.server.spi.GrpcMpExtension would need to be created that contained the names of the service implementations.

Marshalling

Default Marshalling Support

Helidon gRPC supports Protobuf out of the box. The Protobuf marshaller will be used by default for any request and response classes that extend com.google.protobuf.MessageLite, which is the case for all classes generated from a proto file using protoc compiler.

That means that you don’t need any special handling or configuration in order to support Protobuf serialization of requests and responses.

Custom Marshalling

Helidon makes the use of custom marshallers trivial and provides one custom implementation, JsonbMarshaller, out of the box.

You can also easily implement your own marshaller to support serialization formats that are not supported natively by Helidon, by implementing Marshaller and MarshallerSupplier interfaces. As an example, check out the source code of the built-in marshaller: JsonbMarshaller.java.

Furthermore, Oracle Coherence CE provides a marshaller for a highly optimized, binary, platform independent Portable Object Format (POF). You can find more information about POF in Coherence documentation

Setting the custom marshaller

You can annotate your service’s class or interface with @GrpcMarshaller:

Sample code with @GrpcMarshaller annotation
@Grpc
@ApplicationScoped
@GrpcMarshaller("jsonb")  
public class AsyncStringService {
    // code is omitted
}
Copied
  • Set the named marshaller supplier via the @GrpcMarshaller annotation.

Configuration

Configure the gRPC server using the Helidon microprofile configuration framework by which the ConfigSource defaults to microprofile-config.properties. Alternatively, you can also use other ConfigSources such as application.yaml. Refer to MicroProfile Config for more details about the different options for ConfigSources.

Type: io.helidon.grpc.server.GrpcServerConfiguration

Configuration Options

Optional configuration options
keytypedefault valuedescription
name

string

grpc.server

Set the name of the gRPC server.

Configuration key: `name`
native

boolean

false

Specify if native transport should be used.

port

int

1408

Sets server port. If port is 0 or less then any available ephemeral port will be used.

Configuration key: `port`
workers

int

Number of processors available to the JVM

Sets a count of threads in pool used to process HTTP requests. Default value is CPU_COUNT * 2.

Configuration key: `workers`
GrpcServer configuration file example using application.yaml
grpc:
  name: test.server  
  port: 3333  
Copied
  • Specifies the name of the gRPC server.
  • Sets the server port.

Examples

Helidon MP includes some examples that demonstrate the gRPC server usage:

  • Basic gRPC Server example provides a simple gRPC application that deploys a gRPC service that will be discovered by CDI. Two additional services are included that are not normally CDI managed beans, but are manually added as CDI managed beans so that they can also be discovered by Helidon MP.

  • gRPC Server Metrics example demonstrates a Helidon MP application that enables metrics and tracing on a gRPC Service.