听说有个 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 目前还没有。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容