gRPC 官方文檔(Java版)

簡介

gRPC 是一個高性能、開源和通用的 RPC 框架,面向移動和 HTTP/2 設計。目前提供 C、Java 和 Go 語言版本,分別是:grpc, grpc-java, grpc-go. 其中 C 版本支持 C, C++, Node.js, Python, Ruby, Objective-C, PHP 和 C# 支持.

gRPC 基於 HTTP/2 標準設計,帶來諸如雙向流、流控、頭部壓縮、單 TCP 連接上的多複用請求等特。這些特性使得其在移動設備上表現更好,更省電和節省空間佔用。

gRPC 是什麼?

在 gRPC 裡客戶端應用可以像調用本地對象一樣直接調用另一臺不同的機器上服務端應用的方法,使得您能夠更容易地創建分佈式應用和服務。與許多 RPC 系統類似,gRPC 也是基於以下理念:定義一個服務,指定其能夠被遠程調用的方法(包含參數和返回類型)。在服務端實現這個接口,並運行一個 gRPC 服務器來處理客戶端調用。在客戶端擁有一個存根能夠像服務端一樣的方法。

gRPC 官方文檔(Java版)

gRPC 客戶端和服務端可以在多種環境中運行和交互 - 從 google 內部的服務器到你自己的筆記本,並且可以用任何 gRPC 支持的語言來編寫。所以,你可以很容易地用 Java 創建一個 gRPC 服務端,用 Go、Python、Ruby 來創建客戶端。此外,Google 最新 API 將有 gRPC 版本的接口,使你很容易地將 Google 的功能集成到你的應用裡。

使用 protocol buffers

gRPC 默認使用 protocol buffers,這是 Google 開源的一套成熟的結構數據序列化機制(當然也可以使用其他數據格式如 JSON)。正如你將在下方例子裡所看到的,你用 proto files 創建 gRPC 服務,用 protocol buffers 消息類型來定義方法參數和返回類型。你可以在 Protocol Buffers 文檔找到更多關於 Protocol Buffers 的資料。

Protocol buffers 版本

儘管 protocol buffers 對於開源用戶來說已經存在了一段時間,例子內使用的卻一種名叫 proto3 的新風格的 protocol buffers,它擁有輕量簡化的語法、一些有用的新功能,並且支持更多新語言。當前針對 Java 和 C++ 發佈了 beta 版本,針對 JavaNano(即 Android Java)發佈 alpha 版本,在protocol buffers Github 源碼庫裡有 Ruby 支持, 在golang/protobuf Github 源碼庫裡還有針對 Go 語言的生成器, 對更多語言的支持正在開發中。 你可以在 proto3 語言指南里找到更多內容, 在與當前默認版本的發佈說明比較,看到兩者的主要不同點。更多關於 proto3 的文檔很快就會出現。雖然你

可以使用 proto2 (當前默認的 protocol buffers 版本), 我們通常建議你在 gRPC 裡使用 proto3,因為這樣你可以使用 gRPC 支持全部範圍的的語言,並且能避免 proto2 客戶端與 proto3 服務端交互時出現的兼容性問題,反之亦然。

你好 gRPC!

現在你已經對 gRPC 有所瞭解,瞭解其工作機制最簡單的方法是看一個簡單的例子。 Hello World 將帶領你創建一個簡單的客戶端——服務端應用,向你展示:

  • 通過一個 protocol buffers 模式,定義一個簡單的帶有 Hello World 方法的 RPC 服務。
  • 用你最喜歡的語言(如果可用的話)來創建一個實現了這個接口的服務端。
  • 用你最喜歡的(或者其他你願意的)語言來訪問你的服務端。

定義服務

創建我們例子的第一步是定義一個服務:一個 RPC 服務通過參數和返回類型來指定可以遠程調用的方法。就像你在 概覽 裡所看到的, gRPC 通過 protocol buffers 來實現。

我們使用 protocol buffers 接口定義語言來定義服務方法,用 protocol buffer 來定義參數和返回類型。客戶端和服務端均使用服務定義生成的接口代碼。

這裡有我們服務定義的例子,在 helloworld.proto 裡用 protocol buffers IDL 定義的。Greeter 服務有一個方法 SayHello ,可以讓服務端從遠程客戶端接收一個包含用戶名的 HelloRequest 消息後,在一個 HelloReply 裡發送回一個 Greeter。這是你可以在 gRPC 裡指定的最簡單的 RPC - 你可以在教程裡找到針對你選擇的語言更多類型的例子。

syntax = "proto3";
option java_package = "io.grpc.examples";
package helloworld;
// The greeter 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;
}

生成 gRPC 代碼

一旦定義好服務,我們可以使用 protocol buffer 編譯器 protoc 來生成創建應用所需的特定客戶端和服務端的代碼 - 你可以生成任意 gRPC 支持的語言的代碼,當然 PHP 和 Objective-C 僅支持創建客戶端代碼。生成的代碼同時包括客戶端的存根和服務端要實現的抽象接口,均包含 Greeter 所定義的方法。

(假如你沒有在系統裡安裝 gRPC 插件和 protoc ,並且僅僅是要看一下這個例子,你可以跳過這一步,直接到下一步來查看生成的代碼。)

Java

這個例子的構建系統也是 Java gRPC 本身構建的一部分 —— 為了簡單起見,我們推薦使用我們事先生成的例子代碼。你可以參考 README 來看一下如何從你自己的 .proto 文件生成代碼。

這個例子事先生成的代碼在 src/generated/main下。

以下類包含所有我們需要創建這個例子所有的代碼:

  • HelloRequest.java, HelloResponse.java和其他文件包含所有 protocol buffer 用來填充、序列化和提取 HelloRequest 和 HelloReply 消息類型的代碼。
  • GreeterGrpc.java, 包含 (還有其他有用的代碼):
  • Greeter 服務端需要實現的接口
 public static interface Greeter {
public void sayHello(Helloworld.HelloRequest request,
StreamObserver<helloworld.helloreply> responseObserver);
}
/<helloworld.helloreply>

客戶端用來與 Greeter 服務端進行對話的 存根 類。就像你所看到的,異步存根也實現了 Greeter 接口。

 public static class GreeterStub extends AbstractStub<greeterstub>
implements Greeter {
...
}
/<greeterstub>

寫一個服務器

現在讓我們寫點代碼!首先我們將創建一個服務應用來實現服務(你會記起來,我們可以是使用除了Objective-C and PHP 外的其他所有語言來實現)。在本節,我們不打算對如何創建一個服務端進行更深入地探討 —— 更詳細的信息可以在你選擇語言對應的教程裡找到。

服務實現

  • Java

GreeterImpl.java 準確地實現了 Greeter 服務所需要的行為。

正如你所見,GreeterImpl 類通過實現 sayHello 方法,實現了從 IDL 生成的GreeterGrpc.Greeter 接口 。

@Override
public void sayHello(HelloRequest req, StreamObserver<helloreply> responseObserver) {
HelloReply reply = HelloReply.newBuilder().setMessage("Hello " + req.getName()).build();
responseObserver.onNext(reply);
responseObserver.onCompleted();
}
/<helloreply>

sayHello 有兩個參數:

  • HelloRequest,請求。
  • StreamObserver<helloreply>: 應答觀察者,一個特殊的接口,服務器用應答來調用它。/<helloreply>
  • 為了返回給客戶端應答並且完成調用:
  1. 用我們的激動人心的消息構建並填充一個在我們接口定義的 HelloReply 應答對象。
  2. 將 HelloReply 返回給客戶端,然後表明我們已經完成了對 RPC 的處理。

服務端實現

需要提供一個 gRPC 服務的另一個主要功能是讓這個服務實在在網絡上可用。

  • Java

HelloWorldServer.java 提供了以下代碼作為 Java 的例子。

/* The port on which the server should run */
private int port = 50051;
private Server server;
private void start() throws Exception {
server = ServerBuilder.forPort(port)
.addService(GreeterGrpc.bindService(new GreeterImpl()))
.build()
.start();
logger.info("Server started, listening on " + port);

Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
// Use stderr here since the logger may has been reset by its JVM shutdown hook.
System.err.println("*** shutting down gRPC server since JVM is shutting down");
HelloWorldServer.this.stop();
System.err.println("*** server shut down");
}
});
}

在這裡我們創建了合理的 gRPC 服務器,將我們實現的 Greeter 服務綁定到一個端口。然後我們啟動服務器:服務器現在已準備好從 Greeter 服務客戶端接收請求。我們將在具體語言對應的文檔裡更深入地瞭解這所有的工作是怎樣進行的。

寫一個客戶端

客戶端的 gRPC 非常簡單。在這一步,我們將用生成的代碼寫一個簡單的客戶程序來訪問我們在上一節裡創建的 Greeter 服務器。

同樣,我們也不打算對如何實現一個客戶端程序深入更多,我們把這些內容放到教程裡。

連接服務

首先我們看一下我們如何連接 Greeter 服務器。我們需要創建一個 gRPC 頻道,指定我們要連接的主機名和服務器端口。然後我們用這個頻道創建存根實例。

  • Java
private final ManagedChannel channel;
private final GreeterGrpc.GreeterBlockingStub blockingStub;
public HelloWorldClient(String host, int port) {
channel = ManagedChannelBuilder.forAddress(host, port)
.usePlaintext(true)
.build();
blockingStub = GreeterGrpc.newBlockingStub(channel);
}

在這個例子裡,我們創建了一個阻塞的存根。這意味著 RPC 調用要等待服務器應答,將會返回一個應答或拋出一個異常。 gRPC Java 還可以有其他種類的存根,可以向服務器發出非阻塞的調用,這種情況下應答是異步返回的。

調用 RPC

現在我們可以聯繫服務並獲得一個 greeting :

  1. 我們創建並填充一個 HelloRequest 發送給服務。
  2. 我們用請求調用存根的 SayHello(),如果 RPC 成功,會得到一個填充的 HelloReply ,從其中我們可以獲得 greeting。
  • Java
HelloRequest req = HelloRequest.newBuilder().setName(name).build(); 

HelloReply reply = blockingStub.sayHello(req);

試一下!

你可以嘗試用同一個語言在客戶端和服務端構建並運行例子。或者你可以嘗試 gRPC 最有用的一個功能 - 不同的語言間的互操作性,即在不同的語言運行客戶端和服務端。每個服務端和客戶端使用從同一過 proto 文件生成的接口代碼,則意味著任何 Greeter 客戶端可以與任何 Greeter 服務端對話。


分享到:


相關文章: