最近有朋友问我有没有用过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 目前还没有。