聽說有個 GRPC

最近有朋友問我有沒有用過GRPC ,我一直以為RESTful的流行讓 RPC(Remote Procedure Call Protocol) 淡出了人們的視線,記得在幾年前(Adobe還沒退出中國的時候)寫過幾行Flex代碼,寫的還算不糾結,用了LCDS 服務,當時也有免費的BlazeDS ,都算是RPC框架,LCDS提供了更多的服務。

因為不用了解各種底層網絡協議,不用去拼REST風格的動態URL,不用管各種的HTTP狀態碼,并且能夠自動生成客戶端代碼,讓所有需要服務端處理的網絡請求跟調用本地方法一樣簡單,換句話說就是可以直接調用服務器端應用上的方法。LCDS 默認使用AMF協議,使用壓縮的二進制進行傳輸,特別是在請求數據量較大時碾壓XML、JSON等文本格式的傳輸速度。GRPC既然是RPC 框架,特點也不會差太多。下面聊聊GRPC。

GRPC 是什么?

GRPC是一個開源RPC框架,于2015年3月開源,其由Google主要面向移動應用開發并基于HTTP/2協議標準而設計,基于Protobuf 3.0(Protocol Buffers)序列化協議,主流語言都支持。

有什么用呢?其實上面都已經說到了。
在 GRPC 里客戶端應用可以像調用本地對象一樣直接調用另一臺不同的機器上服務端應用的方法,使得更容易地創建分布式應用和服務。與許多 RPC 框架類似,GRPC 也是基于以下理念:定義一個服務,指定其能夠被遠程調用的方法(包含參數和返回類型)。在服務端實現這個接口,并運行一個 GRPC 服務器來處理客戶端調用。在客戶端就能擁有一個像服務端一樣的方法。

沒圖片多無聊

GRPC默認使用Protocol Buffers 這是 Google 開源的一套成熟的結構數據序列化機制(當然也可以使用其他數據格式如 JSON)。
所有GRPC支持的語言編寫的客戶端的網絡層代碼都可以直接交給GRPC生成。

從開發者角度,可以提高開發效率。從用戶角度,客戶端可以充分利用高級流和鏈接功能,從而有助于節省帶寬、降低的TCP鏈接次數、節省CPU使用、和電池壽命。

看上去很屌的樣子。試試...

使用 GRPC

接下來我們完全按照docs中的建議步驟來做
我準備用Objective-C來編寫客戶端,用Node.js來編寫服務端。
在Protobuf 3.0 之前官方是沒有提供Objective-C支持的,需要使用第三方plugin來實現轉換。我們使用Protobuf 3.0版本。
基于性能的原因,生成的OC代碼是沒有使用ARC的,但可以被ARC代碼調用。

setup

開始之前需要安裝Xcode 命令行工具

$ sudo xcode-select --install

雖然文檔中說OS X系統只需要安裝Xcode Commond Line 就夠了,
保險起見(yi ge da keng),還需要安裝 autoconf automake libtool這三個工具,
這里使用brew安裝

$ sudo brew install autoconf automake libtool

安裝grpc

$ git clone https://github.com/grpc/grpc.git 
$ cd grpc $ git submodule update --init 
$ make 
$ make check
$ sudo make install

不出意外的話,安裝算是完成了。
檢查

 $ protoc --version
 libprotoc 3.0.0
Defining a service

用.proto 文件來創建GRPC服務, 用Protobuf消息類型定義方法參數和返回類型。
下面我們定義一個接待服務,把名字傳給服務器,然后服務器發來問候。

syntax = "proto3"; //這里使用proto3的語法規則
option objc_class_prefix = "SKY"; // 定義生成文件的前綴

package helloworld;

// 定義服務
service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// 客戶端傳的名字
message HelloRequest {
  string name = 1;
}

// 服務端返回的消息
message HelloReply {
  string message = 1;
}

服務已經創建好了,下面我們可以通過服務生成客戶端代碼。

Generating grpc code

生成客戶端代碼:

$ protoc -I ./ --objc_out=pbobjc --grpc_out=pbrpc --plugin=protoc-gen-grpc=/usr/local/bin/grpc_objective_c_plugin Greeter.proto

其中 ./ 是我們創建的.proto所在目錄, objc_out 是 Objective-C文件輸出目錄,grpc_out 是基于GRPC 服務的Objective-C文件輸出目錄, plugin 是生成 GRPC 服務的Objective-C文件需要的插件,GRPC 提供了很多生成插件,Greeter.proto是我們創建的接待服務,你可以在后面添加多個需要生成的服務。

執行完成后
會在pbobjc 目錄下生成兩個文件 Greeter.pbobjc.h 、 Greeter.pbobjc.m ,這是基于Protobuf生成的客戶端請求消息
會在pbrpc 目錄下生成兩個文件 Greeter.pborpc.h 、 Greeter.pborpc.m, 這是基于GRPC生成的服務端服務

Writing a server && Service implementation

創建服務端和服務端接口實現
根據proto中的服務定義服務端的接口

service Greeter { 
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

創建server.js

var PROTO_PATH = __dirname + '/../protos/helloworld.proto';

var grpc = require('grpc');
var hello_proto = grpc.load(PROTO_PATH).helloworld;

/**
 * 實現 SayHello RPC 方法
 */
function sayHello(call, callback) {
  callback(null, {message: 'Hello/你好/こんにちは/????? ' + call.request.name});
}

/**
 * 啟動服務
 */
function main() {
  var server = new grpc.Server();
  //注冊SayHello方法
  server.addProtoService(hello_proto.Greeter.service, {sayHello: sayHello});
  server.bind('0.0.0.0:50051', grpc.ServerCredentials.createInsecure());
  server.start();
  console.log("server is listening")
}

main();

創建package.json 安裝依賴

{
  "name": "grpc-examples",
  "version": "0.1.0",
  "dependencies": {
    "async": "^1.5.2",
    "grpc": "0.13.0",
    "lodash": "^4.6.1",
    "minimist": "^1.2.0"
  }
}

安裝依賴,啟動服務。

$ npm install && node server.js
Writing a client && Calling an rpc

編寫客戶端,打開Xcode 創建一個名為HelloWorld的新項目,把剛剛生成四個文件(pbobjc 和 pbprc 目錄)拖進來,然后我們來添加需要的依賴,注:由于生成的文件是非ARC 所以要為生成的文件添加 -fno-objc-arc 編譯參數。
初始化Pod

$ pod init

修改Podfile 添加依賴

platform :ios, '8.0'
pod 'Protobuf', '~> 3.0.0-beta-2'
pod 'gRPC', "~> 0.12"

安裝依賴(需要翻墻)

$ pod install

我們現在算是已經把GRPC集成進客戶端了,現在我們來使用生成的RPC 方法。

我們在ViewController的viewDidLoad 方法中添加如下代碼:

static NSString * const kHostAddress = @"localhost:50051";
[GRPCCall useInsecureConnectionsForHost:kHostAddress];
SKYGreeter *client = [[SKYGreeter alloc] initWithHost:kHostAddress];
SKYHelloRequest *request = [SKYHelloRequest message];
request.name = @"Objective-C";
[client sayHelloWithRequest:request handler:^(SKYHelloReply *response, NSError *error) {
    NSLog(@"%@", response.message);
}];

運行就能看到控制臺的打印了

Hello[47351:333351] Hello/你好/こんにちは/?????  Objective-C

完成!
當然,如果每次都需要手動去生成客戶端代碼還是很麻煩的,我們可以通過CocoaPods 來進行管理,當proto文件變更之后自動生成代碼添加至工程中。
創建 HelloWorld.podspec

Pod::Spec.new do |s|
  s.name     = "HelloWorld"
  s.version  = "0.0.1"
  s.license  = "New BSD"

  s.ios.deployment_target = "7.1"
  s.osx.deployment_target = "10.9"

  # protos 文件目錄
  src = "../protos"

  # 通過proto生成的Objective-C代碼存放目錄
  dir = "Pods/" + s.name

  # 生成客戶端Objective-C代碼
  s.prepare_command = <<-CMD
    mkdir -p #{dir}
    protoc -I #{src} --objc_out=#{dir} --grpc_out=#{dir} --plugin=protoc-gen-grpc=/usr/local/bin/grpc_objective_c_plugin Greeter.proto

  CMD

  s.subspec "Messages" do |ms|
    ms.source_files = "#{dir}/*.pbobjc.{h,m}", "#{dir}/**/*.pbobjc.{h,m}"
    ms.header_mappings_dir = dir
    ms.requires_arc = false
    ms.dependency "Protobuf", "~> 3.0.0-beta-2"
  end

  s.subspec "Services" do |ss|
    ss.source_files = "#{dir}/*.pbrpc.{h,m}", "#{dir}/**/*.pbrpc.{h,m}"
    ss.header_mappings_dir = dir
    ss.requires_arc = true
    ss.dependency "gRPC", "~> 0.12"
    ss.dependency "#{s.name}/Messages"
  end
end

修改 Podfile

platform :ios, '8.0'

pod 'Protobuf', '~> 3.0.0-beta-2'
pod 'gRPC', '~> 0.12'
target 'HelloWorld' do
    pod 'HelloWorld', :path => '.'
end

然后每次修改完 成之后 執行 一下 pod install 就能夠自動生成到Xcode工程中了,是不是很方便。

其實在根據官方Guides 走的過程中遇到了不少坑,各種錯,經過各種嘗試,才把這個簡單的Hello world完成。

GRPC 允許你在proto 定義四種服務方法:

  • request-response,簡單的請求
  • request-stream response,一般用于下載
  • stream request-response,一般用于上傳
  • 雙向流連接,HTTP2.0的特性之一,GRPC 還提供了Full Duplex

關于 proto文件應該怎么寫,文檔有很詳細的說明。

The End

Demo 中只是表現了RPC 的一小部分好處,不難發現,客戶端已經完全拋棄了網絡層,只是通過簡單的方法調用實現了網絡請求,不用去解析JSON ,也不用去判斷HTTP 錯誤碼,服務端也不用再去定義各種路由來接受客戶端的請求,只需要定義實用的接口就可以了,RPC 讓 客戶端和服務端的交互變得更加靈活。

還有:Protobuf已經有第三方插件支持Swift,GRPC 目前還沒有。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容