The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.

Suporte a Threads Virtuais do Quarkus para serviços gRPC

Este guia explica como se beneficiar dos threads virtuais Java ao implementar um serviço gRPC.

Este guia se concentra no uso de threads virtuais com as extensões gRPC. Consulte Escrevendo serviços REST reativos mais simples com o suporte a Threads Virtuais do Quarkus para saber mais sobre os threads virtuais Java em geral e o suporte a Threads Virtuais do Quarkus.

Por padrão, a extensão gRPC do Quarkus invoca métodos de serviço em um thread de loop de evento. Consulte a documentação da Arquitetura Reativa do Quarkus para obter mais detalhes sobre esse tópico. Mas você também pode usar a anotação @Blocking para indicar que o serviço está bloqueando e deve ser executado em um thread de trabalho.

A ideia por trás do suporte a Threads Virtuais do Quarkus para serviços gRPC é descarregar a invocação do método de serviço em threads virtuais, em vez de executá-lo em um thread de loop de evento ou em um thread de trabalho.

Para ativar o suporte a thread virtual em um método de serviço, basta adicionar a anotação @RunOnVirtualThread ao método. Se o JDK for compatível (Java 19 ou versões posteriores - recomendamos 21+), a invocação será transferida para um novo thread virtual. Assim, será possível executar operações de bloqueio sem bloquear o thread da plataforma no qual o thread virtual está montado.

Configurando serviços gRPC para usar threads virtuais

Vamos ver um exemplo de como implementar um serviço gRPC usando threads virtuais. Primeiro, certifique-se de ter a dependência da extensão gRPC em seu arquivo de construção:

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

Você também precisa se certificar de que está usando o Java 19 ou posterior (recomendamos o 21+), o que pode ser garantido no arquivo pom.xml com o seguinte:

pom.xml
<properties>
    <maven.compiler.source>21</maven.compiler.source>
    <maven.compiler.target>21</maven.compiler.target>
</properties>

Execute sua aplicação com:

java -jar target/quarkus-app/quarkus-run.jar

ou para utilizar o modo Quarkus Dev, insira o seguinte na configuração quarkus-maven-plugin:

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>

Em seguida, pode começar a utilizar a anotação @RunOnVirtualThread na implementação do seu serviço:

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());
    }
}
Limitações

Os métodos gRPC que recebem fluxos, como um Multi, não podem utilizar @RunOnVirtualThread, uma vez que o método não deve estar bloqueando e produzir o seu resultado ( Multi ou Uni) imediatamente.

Conteúdo Relacionado