Getting Started with gRPC

此页面说明了如何在 Quarkus 应用程序中开始使用 gRPC。尽管此页面介绍了如何使用 Maven 配置它,但也可以使用 Gradle。

This page explains how to start using gRPC in your Quarkus application. While this page describes how to configure it with Maven, it is also possible to use Gradle.

我们想象您有一个常规的 Quarkus 项目,该项目是从 Quarkus project generator 生成的。默认配置足够使用,但您也可以选择一些扩展(如果您愿意)。

Let’s imagine you have a regular Quarkus project, generated from the Quarkus project generator. The default configuration is enough, but you can also select some extensions if you want.

Solution

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

We recommend that you follow the instructions in the next sections and create the application step by step. However, you can go right to the completed example.

克隆 Git 存储库: git clone {quickstarts-clone-url},或下载 {quickstarts-archive-url}[存档]。

Clone the Git repository: git clone {quickstarts-clone-url}, or download an {quickstarts-archive-url}[archive].

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

The solution is located in the grpc-plain-text-quickstart directory.

Configuring your project

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

Add the Quarkus gRPC extension to your build file:

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)依赖项:

By default, the quarkus-grpc extension relies on the reactive programming model. In this guide we will follow a reactive approach. Under the dependencies section of your pom.xml file, make sure you have the Quarkus REST (formerly RESTEasy Reactive) dependency:

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 插件不需要其他任务/目标。

If you are using Maven, make sure you have the generate-code goal of quarkus-maven-plugin enabled in your pom.xml. If you wish to generate code from different proto files for tests, also add the generate-code-tests goal. Please note that no additional task/goal is required for the Gradle plugin.

<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 文件。

With this configuration, you can put your service and message definitions in the src/main/proto directory. quarkus-maven-plugin will generate Java files from your proto files.

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`指定位置。

quarkus-maven-plugin retrieves a version of protoc (the protobuf compiler) from Maven repositories. The retrieved version matches your operating system and CPU architecture. If this retrieved version does not work in your context, you can either force to use a different OS classifier with -Dquarkus.grpc.protoc-os-classifier=your-os-classifier (e.g. osx-x86_64). You can also download the suitable binary and specify the location via -Dquarkus.grpc.protoc-path=/path/to/protoc.

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

Let’s start with a simple Hello service. Create the src/main/proto/helloworld.proto file with the following content:

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)。

This proto file defines a simple service interface with a single method (SayHello), and the exchanged messages (HelloRequest containing the name and HelloReply containing the greeting message).

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

Your proto file must not contain option java_generic_services = true;. Generic services are deprecated and are not compatible with Quarkus code generation plugins.

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

Before coding, we need to generate the classes used to implement and consume gRPC services. In a terminal, run:

$ mvn compile

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

Once generated, you can look at the target/generated-sources/grpc directory:

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

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

These are the classes we are going to use.

Different gRPC implementations / types

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

Another thing to take note as well is that Quarkus' gRPC support currently includes 3 different types of gRPC usage:

  1. old Vert.x gRPC implementation with a separate gRPC server (default)

  2. new Vert.x gRPC implementation on top of the existing HTTP server

  3. xDS gRPC wrapper over grpc-java with a separate Netty based gRPC server

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

Further docs explain how to enable and use each of them.

Implementing a gRPC service

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

Now that we have the generated classes let’s implement our hello service.

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

With Quarkus, implementing a service requires to implement the generated service interface based on Mutiny, a Reactive Programming API integrated in Quarkus, and expose it as a CDI bean. Learn more about Mutiny on the Mutiny guide. The service class must be annotated with the @io.quarkus.grpc.GrpcService annotation.

Implementing a service

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

Create the src/main/java/org/acme/HelloService.java file with the following content:

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 Expose your implementation as a bean.
2 Implement the generated service interface.
3 Implement the methods defined in the service definition (here we have a single method).

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

You can also use the default gRPC API instead of Mutiny:

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 Expose your implementation as a bean.
2 Extends the ImplBase class. This is a generated class.
3 Implement the methods defined in the service definition (here we have a single method).
4 Build and send the response.
5 Close the response.

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

If your service implementation logic is blocking (use blocking I/O for example), annotate your method with @Blocking. The io.smallrye.common.annotation.Blocking annotation instructs the framework to invoke the annotated method on a worker thread instead of the I/O thread (event-loop).

The gRPC server

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

The services are served by a server. Available services (CDI beans) are automatically registered and exposed.

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

By default, the server is exposed on localhost:9000, and uses plain-text (so no TLS) when running normally, and localhost:9001 for tests.

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

Run the application using: mvn quarkus:dev.

Consuming a gRPC service

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

In this section, we are going to consume the service we expose. To simplify, we are going to consume the service from the same application, which in the real world, does not make sense.

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

Open the existing org.acme.ExampleResource class, and edit the content to become:

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 Inject the service and configure its name. The name is used in the application configuration. If not specified then the field name is used instead: hello in this particular case.
2 Use the generated service interface based on Mutiny API.
3 Invoke the service.

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

We need to configure the application to indicate where the hello service is found. In the src/main/resources/application.properties file, add the following property:

quarkus.grpc.clients.hello.host=localhost
  • hello is the name used in the @GrpcClient annotation.

  • host configures the service host (here it’s localhost).

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

Then, open [role="bare"]http://localhost:8080/hello/quarkus in a browser, and you should get Hello quarkus!

Packaging the application

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

Like any other Quarkus applications, you can package it with: mvn package. You can also package the application into a native executable with: mvn package -Dnative.