Getting Started with gRPC

此页面说明了如何在 Quarkus 应用程序中开始使用 gRPC。尽管此页面介绍了如何使用 Maven 配置它,但也可以使用 Gradle。 我们想象您有一个常规的 Quarkus 项目,该项目是从 Quarkus project generator 生成的。默认配置足够使用,但您也可以选择一些扩展(如果您愿意)。

Solution

我们建议您遵循接下来的部分中的说明,按部就班地创建应用程序。然而,您可以直接跳到完成的示例。

克隆 Git 存储库: git clone $${quickstarts-base-url}.git,或下载 $${quickstarts-base-url}/archive/main.zip[存档]。

解决方案位于 grpc-plain-text-quickstart directory

Configuring your project

在构建文件中添加 Quarkus gRPC 扩展:

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

默认情况下,`quarkus-grpc`扩展依赖于反应式编程模型。在本指南中,我们将遵循反应式方法。在 `pom.xml`文件的 `dependencies`部分下,确保拥有 Quarkus REST(以前称为 RESTEasy Reactive)依赖项:

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

如果你使用的是 Maven,请确保在 `pom.xml`中启用了 `quarkus-maven-plugin`的 `generate-code`目标。如果你希望从不同的 `proto`文件中为测试生成代码,还应添加 `generate-code-tests`目标。请注意,Gradle 插件不需要其他任务/目标。

<build>
    <plugins>
        <plugin>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-maven-plugin</artifactId>
            <version>${quarkus-plugin.version}</version>
            <extensions>true</extensions>
            <executions>
                <execution>
                    <goals>
                        <goal>build</goal>
                        <goal>generate-code</goal>
                        <goal>generate-code-tests</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

通过此配置,你可以将服务和消息定义放入 `src/main/proto`目录中。`quarkus-maven-plugin`将从 `proto`文件中生成 Java 文件。

quarkus-maven-plugin`从 Maven 存储库中检索 `protoc`的版本(protobuf 编译器)。检索到的版本匹配操作系统和 CPU 架构。如果检索到的版本在你的上下文中不起作用,你可以使用 `-Dquarkus.grpc.protoc-os-classifier=your-os-classifier`强制使用不同的 OS 分类器(例如: `osx-x86_64)。你还可以下载合适的二进制文件,并通过 `-Dquarkus.grpc.protoc-path=/path/to/protoc`指定位置。

让我们从一个简单的 _Hello_服务开始。使用以下内容创建 `src/main/proto/helloworld.proto`文件:

syntax = "proto3";

option java_multiple_files = true;
option java_package = "io.quarkus.example";
option java_outer_classname = "HelloWorldProto";

package helloworld;

// The greeting service definition.
service Greeter {
    // Sends a greeting
    rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
    string name = 1;
}

// The response message containing the greetings
message HelloReply {
    string message = 1;
}

这个 proto`文件定义了一个包含单一方法 (`SayHello) 的简单服务接口,以及交换的消息(包含名称的 HelloRequest`和包含问候消息的 `HelloReply)。

你的 proto`文件不得包含 `option java_generic_services = true;Generic services are deprecated与 Quarkus 代码生成插件不兼容。

在编码之前,我们需要生成用于实现和使用 gRPC 服务的类。在终端中,运行:

$ mvn compile

生成之后,你可以查看 `target/generated-sources/grpc`目录:

target/generated-sources/grpc
└── io
    └── quarkus
        └── example
            ├── Greeter.java
            ├── GreeterBean.java
            ├── GreeterClient.java
            ├── GreeterGrpc.java
            ├── HelloReply.java
            ├── HelloReplyOrBuilder.java
            ├── HelloRequest.java
            ├── HelloRequestOrBuilder.java
            ├── HelloWorldProto.java
            └── MutinyGreeterGrpc.java

这些是我们将要使用的类。

Different gRPC implementations / types

还需要注意的另一件事是,Quarkus 的 gRPC 支持目前包括 3 种不同类型的 gRPC 用法:

  1. 具有单独的 gRPC 服务器的旧 Vert.x gRPC 实现(默认)

  2. 在现有 HTTP 服务器顶部的新的 Vert.x gRPC 实现

  3. 具有基于 Netty 的单独 gRPC 服务器的 grpc-java上的 xDS gRPC包装器

进一步的文档解释了如何启用和使用其中的每一个。

Implementing a gRPC service

现在我们有了生成的类,让我们实现我们的 _hello_服务。

使用 Quarkus,实现服务需要实现基于 Mutiny(集成在 Quarkus 中的反应式编程 API)的已生成服务接口,并将其公开为 CDI Bean。在 Mutiny guide中了解有关 Mutiny 的更多信息。服务类必须用 `@io.quarkus.grpc.GrpcService`注解进行注释。

Implementing a service

使用以下内容创建 `src/main/java/org/acme/HelloService.java`文件:

package org.acme;

import io.quarkus.example.Greeter;
import io.quarkus.example.HelloReply;
import io.quarkus.example.HelloRequest;
import io.quarkus.grpc.GrpcService;
import io.smallrye.mutiny.Uni;

@GrpcService 1
public class HelloService implements Greeter {  2

    @Override
    public Uni<HelloReply> sayHello(HelloRequest request) { 3
        return Uni.createFrom().item(() ->
                HelloReply.newBuilder().setMessage("Hello " + request.getName()).build()
        );
    }
}
1 将你的实现公开为 Bean。
2 实现生成的 service 接口。
3 实现服务定义中定义的方法(这里只有一个方法)。

你也可以使用 Mutiny 的默认 gRPC API:

package org.acme;

import io.grpc.stub.StreamObserver;
import io.quarkus.example.GreeterGrpc;
import io.quarkus.example.HelloReply;
import io.quarkus.example.HelloRequest;
import io.quarkus.grpc.GrpcService;

@GrpcService 1
public class HelloService extends GreeterGrpc.GreeterImplBase { 2

    @Override
    public void sayHello(HelloRequest request, StreamObserver<HelloReply> responseObserver) { 3
        String name = request.getName();
        String message = "Hello " + name;
        responseObserver.onNext(HelloReply.newBuilder().setMessage(message).build()); 4
        responseObserver.onCompleted(); 5
    }
}
1 将你的实现公开为 Bean。
2 扩展 ImplBase 类。这是一个生成的类。
3 实现服务定义中定义的方法(这里只有一个方法)。
4 构建并发送响应。
5 Close the response.

如果你的服务实现逻辑为阻塞(例如使用阻塞 I/O),使用 @Blocking 注释你的方法:io.smallrye.common.annotation.Blocking 注释指示框架在工作线程而不是 I/O 线程(事件循环)上调用注释方法。

The gRPC server

services 由 serverserved 公开。可用的 services (CDI beans) 会自动注册和公开。

默认情况下,server 公开于 localhost:9000,并在正常运行时使用 plain-text(因此没有 TLS),并在测试中使用 localhost:9001

使用 mvn quarkus:dev 运行应用程序。

Consuming a gRPC service

在本节中,我们将使用我们公开的服务。为简单起见,我们将从同一个应用程序使用该服务,在实际中这没有意义。

打开现有的 org.acme.ExampleResource 类,并编辑内容为:

package org.acme;

import io.quarkus.example.Greeter;
import io.quarkus.example.HelloRequest;
import io.quarkus.grpc.GrpcClient;
import io.smallrye.mutiny.Uni;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

@Path("/hello")
public class ExampleResource {

    @GrpcClient                               (1)
    Greeter hello;                            (2)

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String hello() {
        return "hello";
    }

    @GET
    @Path("/{name}")
    public Uni<String> hello(String name) {
        return hello.sayHello(HelloRequest.newBuilder().setName(name).build())
                .onItem().transform(helloReply -> helloReply.getMessage());  (3)
    }
}
1 注入服务并配置其名称。名称用于应用程序配置中。如果未指定,则会使用字段名称:在此特定情况下为 hello
2 使用基于 Mutiny API 生成的 service 接口。
3 Invoke the service.

我们需要配置应用程序以指示找到 hello service 的位置。在 src/main/resources/application.properties 文件中,添加以下属性:

quarkus.grpc.clients.hello.host=localhost
  • hello@GrpcClient 注释中使用的名称。

  • host 配置服务 host(这里是 localhost)。

然后,在浏览器中打开 [role="bare"][role="bare"]http://localhost:8080/hello/quarkus,你应该会得到 Hello quarkus

Packaging the application

像任何其他 Quarkus 应用程序一样,你可以使用 mvn package 打包它。你还可以使用 mvn package -Dnative 将应用程序打包成原生可执行文件。