gRPC 入门
本页说明如何在 Quarkus 应用程序中开始使用 gRPC。虽然本页描述了如何使用 Maven 进行配置,但也可以使用 Gradle。
假设您有一个常规的 Quarkus 项目,该项目是从 Quarkus 项目生成器生成的。默认配置已足够,但您也可以选择一些扩展(如果需要)。
解决方案
我们建议您按照以下章节中的说明,逐步创建应用程序。但是,您可以直接转到完整的示例。
克隆 Git 存储库:git clone https://github.com/quarkusio/quarkus-quickstarts.git
,或者下载一个 存档。
解决方案位于 grpc-plain-text-quickstart
目录中。
配置您的项目
将 Quarkus gRPC 扩展添加到您的构建文件中
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-grpc</artifactId>
</dependency>
implementation("io.quarkus:quarkus-grpc")
默认情况下,quarkus-grpc
扩展依赖于响应式编程模型。在本指南中,我们将遵循响应式方法。在 pom.xml
文件的 dependencies
部分下,确保您具有 Quarkus REST(以前称为 RESTEasy Reactive)依赖项
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest</artifactId>
</dependency>
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 体系结构匹配。如果检索到的版本在您的环境中不起作用,您可以强制使用不同的 OS 分类符(通过 -Dquarkus.grpc.protoc-os-classifier=your-os-classifier
,例如 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; 。 通用服务已弃用,并且与 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
这些是我们即将使用的类。
实现 gRPC 服务
现在我们有了生成的类,让我们来实现我们的hello服务。
使用 Quarkus,实现服务需要实现基于 Mutiny(Quarkus 集成的响应式编程 API)的生成服务接口,并将其公开为 CDI bean。在 Mutiny 指南中了解更多关于 Mutiny 的信息。服务类必须用 @io.quarkus.grpc.GrpcService
注释进行注解。
实现服务
创建 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 | 实现生成的服务接口。 |
3 | 实现服务定义中定义的方法(这里我们只有一个方法)。 |
您也可以使用默认的 gRPC API 而不是 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 | 将您的实现公开为 bean。 |
2 | 扩展 ImplBase 类。这是一个生成的类。 |
3 | 实现服务定义中定义的方法(这里我们只有一个方法)。 |
4 | 构建并发送响应。 |
5 | 关闭响应。 |
如果您的服务实现逻辑是阻塞的(例如,使用阻塞 I/O),请用 @Blocking 注释您的方法。io.smallrye.common.annotation.Blocking 注释指示框架在工作线程而不是 I/O 线程(事件循环)上调用被注解的方法。 |
消耗 gRPC 服务
在本节中,我们将消耗我们公开的服务。为简化起见,我们将从同一个应用程序消耗该服务,这在实际世界中是没有意义的。
打开现有的 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 的生成服务接口。 |
3 | 调用服务。 |
我们需要配置应用程序以指示 hello
服务的位置。在 src/main/resources/application.properties
文件中,添加以下属性
quarkus.grpc.clients.hello.host=localhost
-
hello
是在@GrpcClient
注释中使用的名称。 -
host
配置服务主机(此处为 localhost)。
然后,在浏览器中打开 https://:8080/hello/quarkus,您应该会收到 Hello quarkus
!