1. 總覽
gRPC 是由 Google 開發的遠端程序呼叫 (RPC) 架構和工具集,支援各種語言、平台中立的遠端程序呼叫,可讓您使用通訊協定緩衝區 (通訊協定緩衝區) 定義服務。通訊協定緩衝區是特別強大的二進位序列化工具集和語言。然後,您就可以透過各種語言的服務定義,產生慣用的用戶端和伺服器虛設常式。
在本程式碼研究室中,您將學習如何建構以 gRPC 架構公開 API 的 Java 服務,然後編寫用戶端來使用產生的 gRPC 用戶端虛設常式。
課程內容
- 通訊協定緩衝區語言
- 如何使用 Java 實作 gRPC 服務
- 如何使用 Java 實作 gRPC 用戶端
您會如何使用這個教學課程?
針對建構 Node.js 應用程式的經驗,您會給予什麼評價?
針對建構 Go 應用程式的經驗,您會給予什麼評價?
2. 設定和需求
自修環境設定
提醒您,專案 ID 是所有 Google Cloud 專案的專屬名稱 (已經有人使用上述名稱,很抱歉對您不符!)。稍後在本程式碼研究室中會稱為 PROJECT_ID
。
- 接下來,您需要在 Cloud 控制台中啟用計費功能,才能使用 Google Cloud 資源。
執行這個程式碼研究室並不會產生任何費用,如果有的話。請務必依照「清除所用資源」一節指示本節將說明如何關閉資源,這樣您就不會產生本教學課程結束後產生的費用。Google Cloud 的新使用者符合 $300 美元免費試用計畫的資格。
Google Cloud Shell
雖然這個程式碼研究室可以透過電腦操作,但在本程式碼研究室中,我們會使用 Google Cloud Shell,這是一種在 Cloud 中執行的指令列環境。
這種以 Debian 為基礎的虛擬機器,搭載各種您需要的開發工具。提供永久的 5 GB 主目錄,而且在 Google Cloud 中運作,大幅提高網路效能和驗證能力。換言之,本程式碼研究室只需要在 Chromebook 上運作即可。
- 如要透過 Cloud 控制台啟用 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 控制台資訊主頁查詢:
根據預設,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 語法來定義訊息酬載和作業。我們來為包含 Hello Request 和 Hello Response 的簡易 Greeting Service 建立 proto 檔案。
首先,請建立新的 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
實作服務
首先,請建立新的 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 轉碼
請提供您寶貴的意見
- 請花點時間完成簡短的問卷調查