1. 概览
gRPC 是 Google 开发的与语言和平台无关的远程过程调用 (RPC) 框架和工具集。它支持您使用 Protocol Buffers(一种特别强大的二进制序列化工具和语言)定义服务。然后,您可以使用各种语言根据服务定义生成惯用的客户端和服务器存根。
在此 Codelab 中,您将学习如何构建基于 Java 的服务(使用 gRPC 框架公开 API),然后编写客户端以使用生成的 gRPC 客户端桩。
学习内容
- 协议缓冲区语言
- 如何使用 Java 实现 gRPC 服务
- 如何使用 Java 实现 gRPC 客户端
您将如何使用本教程?
您如何评价自己在构建 Node.js 应用方面的经验水平?
您如何评价自己在构建 Go 应用方面的经验水平?
<ph type="x-smartling-placeholder">2. 设置和要求
自定进度的环境设置



请记住项目 ID,它在所有 Google Cloud 项目中都是唯一的名称(上述名称已被占用,您无法使用,抱歉!)。它稍后将在此 Codelab 中被称为 PROJECT_ID。
- 接下来,您需要在 Cloud 控制台中启用结算功能,才能使用 Google Cloud 资源。
运行此 Codelab 应该不会产生太多的费用(如果有费用的话)。请务必按照“清理”部分部分,其中会指导您如何关停资源,以免产生超出本教程范围的结算费用。Google Cloud 的新用户符合参与 300 美元的免费试用计划的条件。
Google Cloud Shell
虽然此 Codelab 可以在计算机上操作,但在此 Codelab 中,我们将使用 Google Cloud Shell,这是一个在云端运行的命令行环境。
基于 Debian 的这个虚拟机已加载了您需要的所有开发工具。它提供了一个持久的 5GB 主目录,并且在 Google Cloud 中运行,大大增强了网络性能和身份验证。这意味着在本 Codelab 中,您只需要一个浏览器(没错,它适用于 Chromebook)。
- 如需从 Cloud Console 激活 Cloud Shell,只需点击激活 Cloud Shell
(预配和连接到环境仅需花费一些时间)。


在连接到 Cloud Shell 后,您应该会看到自己已通过身份验证,并且相关项目已设置为您的 PROJECT_ID。
gcloud auth list
命令输出
Credentialed accounts: - <myaccount>@<mydomain>.com (active)
gcloud config list project
命令输出
[core] project = <PROJECT_ID>
如果出于某种原因未设置项目,只需发出以下命令即可:
gcloud config set project <PROJECT_ID>
正在查找您的 PROJECT_ID?检查您在设置步骤中使用的 ID,或在 Cloud Console 信息中心查找该 ID:

默认情况下,Cloud Shell 还会设置一些环境变量,这对您日后运行命令可能会很有用。
echo $GOOGLE_CLOUD_PROJECT
命令输出
<PROJECT_ID>
- 最后,设置默认可用区和项目配置。
gcloud config set compute/zone us-central1-f
您可以选择各种不同的可用区。如需了解详情,请参阅区域和可用区。
3. 创建 gRPC 服务
使用 Maven 创建一个新的 Java 项目:
$ mvn archetype:generate -DgroupId=com.example.grpc \ -DartifactId=grpc-hello-server \ -DarchetypeArtifactId=maven-archetype-quickstart \ -DinteractiveMode=false $ cd grpc-hello-server
添加 gRPC 定义文件
在 gRPC 中,服务载荷(请求和响应)和服务操作需要以 IDL(接口定义语言)捕获。gRPC 使用 Protobuffer 3 语法定义消息载荷和操作。让我们为简单的 Greeting Service 创建一个 proto 文件,其中包含 Hello 请求和 Hello 响应。
首先,创建一个新的 proto 目录以存储新的 proto 文件:
$ mkdir -p src/main/proto
然后,创建一个新的 proto 文件 src/main/proto/GreetingService.proto。
您可以使用 vim,nano, 或 emacs 修改该文件:
src/main/proto/GreetingService.proto
syntax = "proto3";
package com.example.grpc;
// Request payload
message HelloRequest {
// Each message attribute is strongly typed.
// You also must assign a "tag" number.
// Each tag number is unique within the message.
string name = 1;
// This defines a strongly typed list of String
repeated string hobbies = 2;
// There are many more basics types, like Enum, Map
// See https://developers.google.com/protocol-buffers/docs/proto3
// for more information.
}
message HelloResponse {
string greeting = 1;
}
// Defining a Service, a Service can have multiple RPC operations
service GreetingService {
// Define a RPC operation
rpc greeting(HelloRequest) returns (HelloResponse);
}
添加 gRPC 依赖项和插件
获得定义后,我们就可以从该文件生成服务器端存根和客户端存根。您需要添加 gRPC 依赖项和插件。
首先,将 gRPC 依赖项添加到 pom.xml:
pom.xml
<project>
...
<dependencies>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty-shaded</artifactId>
<version>1.24.0</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
<version>1.24.0</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
<version>1.24.0</version>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
...
</dependencies>
...
</project>
然后添加插件:
pom.xml
<project>
...
<dependencies>
...
</dependencies>
<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.6.2</version>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.6.1</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:3.9.0:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:1.24.0:exe:${os.detected.classifier}</pluginArtifact>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
生成存根
在构建应用时,插件会将 proto 定义转换为 Java 代码。
$ mvn -DskipTests package
如需查看生成的文件,请执行以下操作:
$ find target/generated-sources
实现 Service
首先,创建一个新的 GreetingServiceImpl 类来实现 greeting 操作:
src/main/java/com/example/grpc/GreetingServiceImpl.java
package com.example.grpc;
import io.grpc.stub.StreamObserver;
public class GreetingServiceImpl extends GreetingServiceGrpc.GreetingServiceImplBase {
@Override
public void greeting(GreetingServiceOuterClass.HelloRequest request,
StreamObserver<GreetingServiceOuterClass.HelloResponse> responseObserver) {
// HelloRequest has toString auto-generated.
System.out.println(request);
// You must use a builder to construct a new Protobuffer object
GreetingServiceOuterClass.HelloResponse response = GreetingServiceOuterClass.HelloResponse.newBuilder()
.setGreeting("Hello there, " + request.getName())
.build();
// Use responseObserver to send a single response back
responseObserver.onNext(response);
// When you are done, you must call onCompleted.
responseObserver.onCompleted();
}
}
实现服务器
最后,您需要启动服务器以监听端口并注册此服务实现。修改 App 类及其主方法:
src/main/java/com/example/grpc/App.java
package com.example.grpc;
import io.grpc.*;
public class App
{
public static void main( String[] args ) throws Exception
{
// Create a new server to listen on port 8080
Server server = ServerBuilder.forPort(8080)
.addService(new GreetingServiceImpl())
.build();
// Start the server
server.start();
// Server threads are running in the background.
System.out.println("Server started");
// Don't exit the main thread. Wait until server is terminated.
server.awaitTermination();
}
}
最后,运行服务器:
$ mvn -DskipTests package exec:java -Dexec.mainClass=com.example.grpc.App ... Server Started
4. 使用服务
生成器已经生成了所有客户端存根。为简单起见,我们将使用同一 Maven 项目,只不过添加了一个新的 Client 类和一个新的主方法。
首先,点击 + 打开新的 Cloud Shell 会话,这样您就无需终止服务器:

在新会话中,切换到 grpc-hello-server 目录:
$ cd grpc-hello-server
然后,添加新的 Client 类:
src/main/java/com/example/grpc/Client.java
package com.example.grpc;
import io.grpc.*;
public class Client
{
public static void main( String[] args ) throws Exception
{
// Channel is the abstraction to connect to a service endpoint
// Let's use plaintext communication because we don't have certs
final ManagedChannel channel = ManagedChannelBuilder.forTarget("localhost:8080")
.usePlaintext(true)
.build();
// It is up to the client to determine whether to block the call
// Here we create a blocking stub, but an async stub,
// or an async stub with Future are always possible.
GreetingServiceGrpc.GreetingServiceBlockingStub stub = GreetingServiceGrpc.newBlockingStub(channel);
GreetingServiceOuterClass.HelloRequest request =
GreetingServiceOuterClass.HelloRequest.newBuilder()
.setName("Ray")
.build();
// Finally, make the call using the stub
GreetingServiceOuterClass.HelloResponse response =
stub.greeting(request);
System.out.println(response);
// A Channel should be shutdown before stopping the process.
channel.shutdownNow();
}
}
最后,运行客户端:
$ mvn -DskipTests package exec:java -Dexec.mainClass=com.example.grpc.Client ... greeting: "Hello there, Ray"
大功告成!很简单,对吧?
5. 在线媒体服务
还有很多功能可以尝试。例如,您只需将 proto 文件中的 stream 关键字添加到请求或响应参数中,即可轻松构建流服务。例如
src/main/proto/GreetingService.proto
syntax = "proto3";
package com.example.grpc;
...
// Defining a Service, a Service can have multiple RPC operations
service GreetingService {
// MODIFY HERE: Update the return to streaming return.
rpc greeting(HelloRequest) returns (stream HelloResponse);
}
更新您的服务器以发送多个响应,而不是仅发送一个。为此,您可以进行多次 responseObserver.onNext(...) 调用:
src/main/java/com/example/grpc/GreetingServiceImpl.java
package com.example.grpc;
import io.grpc.stub.StreamObserver;
public class GreetingServiceImpl extends GreetingServiceGrpc.GreetingServiceImplBase {
@Override
public void greeting(GreetingServiceOuterClass.HelloRequest request,
StreamObserver<GreetingServiceOuterClass.HelloResponse> responseObserver) {
...
// Feel free to construct different responses if you'd like.
responseObserver.onNext(response);
responseObserver.onNext(response);
responseObserver.onNext(response);
// When you are done, you must call onCompleted.
responseObserver.onCompleted();
}
}
客户端必须使用异步存根,而不是阻塞存根。更新客户端代码,确保将 stub 类型更新为 GreetingServiceStub:
src/main/java/com/example/grpc/Client.java
package com.example.grpc;
import io.grpc.*;
// New import
import io.grpc.stub.*;
public class Client
{
public static void main( String[] args ) throws Exception
{
final ManagedChannel channel = ManagedChannelBuilder.forTarget("localhost:8080")
.usePlaintext(true)
.build();
// Replace the previous synchronous code with asynchronous code.
// This time use an async stub:
GreetingServiceGrpc.GreetingServiceStub stub = GreetingServiceGrpc.newStub(channel);
// Construct a request
GreetingServiceOuterClass.HelloRequest request =
GreetingServiceOuterClass.HelloRequest.newBuilder()
.setName("Ray")
.build();
// Make an Asynchronous call. Listen to responses w/ StreamObserver
stub.greeting(request, new StreamObserver<GreetingServiceOuterClass.HelloResponse>() {
public void onNext(GreetingServiceOuterClass.HelloResponse response) {
System.out.println(response);
}
public void onError(Throwable t) {
}
public void onCompleted() {
// Typically you'll shutdown the channel somewhere else.
// But for the purpose of the lab, we are only making a single
// request. We'll shutdown as soon as this request is done.
channel.shutdownNow();
}
});
}
}
重新构建应用:
$ mvn -DskipTests package
在服务器和客户端各自的 Cloud Shell 会话中分别重启它们。
如需启动服务器,请执行以下操作:
$ mvn exec:java -Dexec.mainClass=com.example.grpc.App ... Server Started
如需启动客户端,请执行以下操作:
$ mvn exec:java -Dexec.mainClass=com.example.grpc.Client ... greeting: "Hello there, Ray" greeting: "Hello there, Ray" greeting: "Hello there, Ray"
6. 恭喜!
所学内容:
- 协议缓冲区语言
- 如何使用 Java 实现 gRPC 服务器
- 如何使用 Java 实现 gRPC 客户端
后续步骤:
- 详细了解 gRPC Java
- 在 GitHub 上查看更多 gRPC Java 示例
- 了解在 gRPC 中流式传输
- 详细了解从 gRPC 到 REST 的转码
向我们提供反馈
- 请抽空完成我们简短的调查问卷