-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #34855 from cescoffier/gRPC-Virtual-threads
Implement support for @RunOnVirtualThread for gRPC services
- Loading branch information
Showing
24 changed files
with
1,303 additions
and
62 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
= Quarkus Virtual Thread support for gRPC services | ||
|
||
include::_attributes.adoc[] | ||
:runonvthread: https://javadoc.io/doc/io.smallrye.common/smallrye-common-annotation/latest/io/smallrye/common/annotation/RunOnVirtualThread.html | ||
:blocking_annotation: https://javadoc.io/doc/io.smallrye.reactive/smallrye-reactive-messaging-api/latest/io/smallrye/reactive/messaging/annotations/Blocking.html | ||
|
||
This guide explains how to benefit from Java virtual threads when implementing a gRPC service. | ||
|
||
[TIP] | ||
==== | ||
This guide focuses on using virtual threads with the gRPC extensions. | ||
Please refer to xref:virtual-threads.adoc[Writing simpler reactive REST services with Quarkus Virtual Thread support] | ||
to read more about Java virtual threads in general and the Quarkus Virtual Thread support. | ||
==== | ||
|
||
By default, the Quarkus gRPC extension invokes service methods on an event-loop thread. | ||
See the xref:quarkus-reactive-architecture.adoc[Quarkus Reactive Architecture documentation] for further details on this topic. | ||
But, you can also use the link:{blocking_annotation}[@Blocking] annotation to indicate that the service is _blocking_ and should be run on a worker thread. | ||
|
||
The idea behind Quarkus Virtual Thread support for gRPC services is to offload the service method invocation on virtual threads, instead of running it on an event-loop thread or a worker thread. | ||
|
||
To enable virtual thread support on a service method, simply add the link:{runonvthread}[@RunOnVirtualThread] annotation to the method. | ||
If the JDK is compatible (Java 19 or later versions) then the invocation will be offloaded to a new virtual thread. | ||
It will then be possible to perform blocking operations without blocking the platform thread upon which the virtual thread is mounted. | ||
|
||
== Configuring gRPC services to use virtual threads | ||
|
||
Let's see an example of how to implement a gRPC service using virtual threads. | ||
First, make sure to have the gRPC extension dependency in your build file: | ||
|
||
[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"] | ||
.pom.xml | ||
---- | ||
<dependency> | ||
<groupId>io.quarkus</groupId> | ||
<artifactId>quarkus-grpc</artifactId> | ||
</dependency> | ||
---- | ||
|
||
[source,gradle,role="secondary asciidoc-tabs-target-sync-gradle"] | ||
.build.gradle | ||
---- | ||
implementation("io.quarkus:quarkus-grpc") | ||
---- | ||
|
||
You also need to make sure that you are using Java 19 or later, this can be enforced in your `pom.xml` file with the following: | ||
|
||
[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"] | ||
.pom.xml | ||
---- | ||
<properties> | ||
<maven.compiler.source>19</maven.compiler.source> | ||
<maven.compiler.target>19</maven.compiler.target> | ||
</properties> | ||
---- | ||
|
||
Virtual threads are still a preview feature, so you need to start your application with the `--enable-preview` flag: | ||
|
||
[source, bash] | ||
---- | ||
java --enable-preview -jar target/quarkus-app/quarkus-run.jar | ||
---- | ||
|
||
or to use the Quarkus Dev mode, insert the following to the `quarkus-maven-plugin` configuration: | ||
|
||
[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"] | ||
.pom.xml | ||
---- | ||
<plugin> | ||
<groupId>io.quarkus</groupId> | ||
<artifactId>quarkus-maven-plugin</artifactId> | ||
<version>${quarkus.version}</version> | ||
<executions> | ||
<execution> | ||
<goals> | ||
<goal>build</goal> | ||
<goal>generate-code</goal> | ||
<goal>generate-code-tests</goal> | ||
</goals> | ||
</execution> | ||
</executions> | ||
<configuration> | ||
<source>19</source> | ||
<target>19</target> | ||
<compilerArgs> | ||
<arg>--enable-preview</arg> | ||
</compilerArgs> | ||
</configuration> | ||
</plugin> | ||
---- | ||
|
||
NOTE: The `--enable-preview` flag is not necessary with Java 21+. | ||
|
||
Then you can start using the annotation `@RunOnVirtualThread` in your service implementation: | ||
|
||
[source, java] | ||
---- | ||
package io.quarkus.grpc.example.streaming; | ||
import com.google.protobuf.ByteString; | ||
import com.google.protobuf.EmptyProtos; | ||
import io.grpc.testing.integration.Messages; | ||
import io.grpc.testing.integration.TestService; | ||
import io.quarkus.grpc.GrpcService; | ||
import io.smallrye.common.annotation.RunOnVirtualThread; | ||
import io.smallrye.mutiny.Multi; | ||
import io.smallrye.mutiny.Uni; | ||
@GrpcService | ||
public class TestServiceImpl implements TestService { | ||
@RunOnVirtualThread | ||
@Override | ||
public Uni<EmptyProtos.Empty> emptyCall(EmptyProtos.Empty request) { | ||
return Uni.createFrom().item(EmptyProtos.Empty.newBuilder().build()); | ||
} | ||
@RunOnVirtualThread | ||
@Override | ||
public Uni<Messages.SimpleResponse> unaryCall(Messages.SimpleRequest request) { | ||
var value = request.getPayload().getBody().toStringUtf8(); | ||
var resp = Messages.SimpleResponse.newBuilder() | ||
.setPayload(Messages.Payload.newBuilder().setBody(ByteString.copyFromUtf8(value.toUpperCase())).build()) | ||
.build(); | ||
return Uni.createFrom().item(resp); | ||
} | ||
@Override | ||
@RunOnVirtualThread | ||
public Multi<Messages.StreamingOutputCallResponse> streamingOutputCall(Messages.StreamingOutputCallRequest request) { | ||
var value = request.getPayload().getBody().toStringUtf8(); | ||
return Multi.createFrom().<String> emitter(emitter -> { | ||
emitter.emit(value.toUpperCase()); | ||
emitter.emit(value.toUpperCase()); | ||
emitter.emit(value.toUpperCase()); | ||
emitter.complete(); | ||
}).map(v -> Messages.StreamingOutputCallResponse.newBuilder() | ||
.setPayload(Messages.Payload.newBuilder().setBody(ByteString.copyFromUtf8(v)).build()) | ||
.build()); | ||
} | ||
} | ||
---- | ||
|
||
[IMPORTANT] | ||
.Limitations | ||
==== | ||
The gRPC methods receiving _streams_, such as a `Multi` cannot use `@RunOnVirtualThread`, as the method must not be blocking and produce its result (`Multi` or `Uni`) immediately. | ||
==== |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.