The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.
このページを編集

Quarkus の仮想スレッドによる gRPC サービスのサポート

このガイドでは、gRPC サービスを実装する際に Java 仮想スレッドを利用する方法を説明します。

このガイドでは、gRPC エクステンションを使用した仮想スレッドの使用に焦点を当てます。 Java 仮想スレッド全般と Quarkus 仮想スレッドのサポートについて、詳しくは Quarkus 仮想スレッドのサポートを使用してシンプルなりアクティブ RESTORE サービスを作成する を参照してください。

デフォルトでは、Quarkus gRPC エクステンションはイベントループスレッドでサービスメソッドを呼び出します。 このトピックの詳細は、Quarkus リアクティブアーキテクチャーのドキュメント を参照してください。 ただし、@Blocking アノテーションを使用して、サービスが ブロッキング状態 であり、ワーカースレッドで実行する必要があることを示すこともできます。

Quarkus の gRPC サービスにおける仮想スレッドのサポートは、サービスメソッドの呼び出しをイベントループスレッドまたはワーカースレッドで実行するのではなく、仮想スレッドにオフロードすることを目的としています。

サービスメソッドで仮想スレッドのサポートを有効にするには、@RunOnVirtualThread アノテーションをメソッドに追加します。 JDK に互換性がある場合 (Java 19 以降のバージョン - 推奨は 21 以降)、呼び出しは新しい仮想スレッドにオフロードされます。 こうすることで、仮想スレッドがマウントされているプラットフォームスレッドをブロックせずに、ブロッキング操作を実行できるようになります。

仮想スレッドを使用するように gRPC サービスを設定する

仮想スレッドを使用した gRPC サービスの実装方法について説明します。 まず、ビルドファイルに gRPC エクステンションの依存関係があることを確認します。

pom.xml
<dependency>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-grpc</artifactId>
</dependency>
build.gradle
implementation("io.quarkus:quarkus-grpc")

Java 19 以降 (推奨は 21 以降) を使用していることを確認する必要があります。これは、次のように pom.xml ファイルで強制できます。

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 開発モードを使用します。

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>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());
    }
}
制約事項

Multi などの streams を受信する gRPC メソッドは、メソッドがブロッキングや結果 (Multi または Uni) の即時生成を実行してはならないため、 @RunOnVirtualThread は使用できません。

関連コンテンツ