Just to be clear, as there seems to be a misunderstanding: Helidon gRPC doesn't *replace*
grpc-java, or defines a "new" gRPC standard at the transport level, which is the only thing gRPC itself (via CNCF) standardizes. It uses
grpc-java under the hood, and allows you to implement both the services and the clients in a way that is more along the lines of JAX-RS and MP REST Client, which we feel is cleaner and easier to grok by the average Java developer, and more consistent with the existing technologies in the MicroProfile stack.
Ultimately, gRPC in general, and grpc-java as one of its implementations, boil down to 4 supported method types: unary, server streaming, client streaming and bidirectional. Helidon gRPC allows you to define any of those by annotating Java method on a POJO with @Unary, @ServerStreaming, @ClientStreaming or @BiDirectional, just like JAX-RS allows you to define handlers for various HTTP methods via @Get, @Post, @Put, @Delete, etc.
Then there is also syntactic sugar we added to make the implementation simpler. Instead of always having to deal with gRPC StreamObserver, which tends to be cumbersome to use for by far the most common use case of unary methods, you can simply return the value and we'll complete the StreamObserver for you.
In other words, while you could define a method such as:
@Unary
public void hello(String name, StreamObserver<String> observer) {
observer.onNext("Hello, " + name);
observer.onCompleted();
}
it is simpler to write
@Unary
public String hello(String name) {
return "Hello, " + name;
}
Similarly, for server streaming methods you can simply annotate it with @ServerStreaming, and return Stream or Iterable, and we'll do the right thing under the hood. I really don't see the point of types such as Uni or Multi that Quarkus provides, when Java already has a perfectly good way to represent both.
Of course, if you really want you can always use StreamObserver directly as well, just like you can use Response class in JAX-RS to have full control over the response.
Finally, by wrapping return type for any method with a CompletionStage, you turn the service implementation into an async one:
@Unary
public CompletionStage<String> hello(String name) {
return CompletableFuture.completedFuture("Hello, " + name);
// pointless, but you get the idea -- it works very well with async and reactive backends
}
As for which requires less code, I'd rather write Java code than proto IDL, especially because the later *forces* you to use Protobuf as a marshaller. Not to mention that it requires that you configure protoc and gRPC compiler in your POM or Gradle file, generates a ton of mostly unreadable code, etc.
We support all of that, but don't require it. For example, the code above won't work with Protobuf, because String is not a Protobuf Message, but it will work perfectly fine with many other serialization formats, such as Java serialization, JSONB and POF, that don't require special classes to be used as messages.
In the end, that's really all we are proposing when it comes to MP gRPC spec -- a set of developer-friendly annotations that allow you to implement gRPC services and clients in a way that is similar to what we are all used to from REST and JAX-RS, and a way to control marshaling so serialization formats other than Protobuf can be used (which is something gRPC itself supports, but the existing tooling doesn't).
- Aleks