Quarkus の仮想スレッドによる gRPC サービスのサポート
このガイドでは、gRPC サービスを実装する際に Java 仮想スレッドを利用する方法を説明します。
このガイドでは、gRPC エクステンションを使用した仮想スレッドの使用に焦点を当てます。 Java 仮想スレッド全般と Quarkus 仮想スレッドのサポートについて、詳しくは Quarkus 仮想スレッドのサポートを使用してシンプルなりアクティブ RESTORE サービスを作成する を参照してください。 |
デフォルトでは、Quarkus gRPC エクステンションはイベントループスレッドでサービスメソッドを呼び出します。 このトピックの詳細は、Quarkus リアクティブアーキテクチャーのドキュメント を参照してください。 ただし、@Blocking アノテーションを使用して、サービスが ブロッキング状態 であり、ワーカースレッドで実行する必要があることを示すこともできます。
Quarkus の gRPC サービスにおける仮想スレッドのサポートは、サービスメソッドの呼び出しをイベントループスレッドまたはワーカースレッドで実行するのではなく、仮想スレッドにオフロードすることを目的としています。
サービスメソッドで仮想スレッドのサポートを有効にするには、@RunOnVirtualThread アノテーションをメソッドに追加します。 JDK に互換性がある場合 (Java 19 以降のバージョン - 推奨は 21 以降)、呼び出しは新しい仮想スレッドにオフロードされます。 こうすることで、仮想スレッドがマウントされているプラットフォームスレッドをブロックせずに、ブロッキング操作を実行できるようになります。
仮想スレッドを使用するように gRPC サービスを設定する
仮想スレッドを使用した gRPC サービスの実装方法について説明します。 まず、ビルドファイルに gRPC エクステンションの依存関係があることを確認します。
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-grpc</artifactId>
</dependency>
implementation("io.quarkus:quarkus-grpc")
Java 19 以降 (推奨は 21 以降) を使用していることを確認する必要があります。これは、次のように pom.xml
ファイルで強制できます。
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
</properties>
次のコマンドでアプリケーションを実行します。
java -jar target/quarkus-app/quarkus-run.jar
または、 quarkus-maven-plugin
設定に以下を注入して Quarkus 開発モードを使用します。
<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>21</source>
<target>21</target>
</configuration>
</plugin>
これで、サービス実装で @RunOnVirtualThread
アノテーションを使用できるようになりました。
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());
}
}
制約事項
|