前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >grpc-swift入门

grpc-swift入门

作者头像
iOS Development
发布2022-06-12 15:32:42
1.1K0
发布2022-06-12 15:32:42
举报

先戴个头盔,以下所有论述不保证正确性,请自行甄别服用。

不想看前面的屁话,要直接上代码的,请跳到「iOS App端如何实现和RPC服务器通信」章节

什么是RPC、gRPC、grpc-swift

要搞清楚什么是grpc-swift

就要先搞清楚什么是gRPC

要搞清楚什么是gRPC,

就要先搞清楚什么是RPC

What is RPC

RPC,是Remote procedure call的简称,翻译过来——「远程过程调用」。

请讲人话?OK,举个🌰,假如我要转1个比特币给你(事实上我并没有1个比特币,不嫌弃波卡幣/Polkadot的话,可以转给你——最近跌好惨🫠),然后我就通过RPC这种「传输方式」转给你。

本质上这是一个传输数据的过程。所以RPC,就简单理解成「一种传输数据的方式」。

对比地看,我们还有另一种更常用的方式:HTTP+REST。(不知道啥玩意儿?不要紧。就理解成是互联网上另一种传输数据的方式就好了。)

简单来说,HTTP+REST方式,聚焦在数据data上:发送一个请求request,然后返回数据response

而RPC,聚焦在「方法」上——直接调用一个「方法/函数/command」——只是对比于在同一个软件内部调用方法,RPC中调用有点不太一样,它是从电脑A,直接调用电脑B中的某个「方法」,是一个远程调用(Remote Call)。

然后这个「方法」和我们常见的「方法」一样,会有参数、返回值。要传输的数据,就放在参数、返回值里面,最终实现数据的传输。如下图:

RPC的数据传输过程

截图出处: Comparing web API types: SOAP, REST, GraphQL and RPC

What is gRPC

OK,RPC是一种传输数据的方式,那gRPC又是什么?

聪明的你意识到了,这里多了一个「g」。不过,大多数人第一反应,应该不会认为「g」表示的是「Google」——毕竟百度是众多宇宙中第一好用的搜索引擎。

事实上「g」表示的,正是Google(起码大多数人是这样认为的。关于「g」的其他含义,下面再作补充),gRPC是Google主导的对RPC的具体实现。卖点:高性能、开源、通用(支持很多语言: Supported languages)。

其中,Protocol Buffers,有需要认识一下的。可以把它类比成XML、JSON,但是Protocol Buffers的数据包更小、速度更快、实现更简单。

你可能会猜到,RPC还有XML-RPCJSON-RPC这些其他的实现。而gRPC,更准确的对标,我觉得应该叫「Protocol Buffers-RPC」~

再回到「g」,事实上,把它理解成「Google」没有错,不过,经常没事找抽的工程师,对「g」是有另一番调侃的,详情: GRPC Core: g_stands_for

为了让大家有更直观的理解,下面把互联网数据传输的一部分发展史展现如下:

history

What is grpc-swift

OK,我们有gRPC了,是不是可以开始写iOS端的App,从「RPC后台」拿一些数据了? 上面提到,gRPC支持多种语言,其中就有Objective-C(如果暂时不理解「支持」的含义,后面会继续解释)。

但是,现在大家都用Swift开发iOS App,所以就有了grpc-swift了。

所以,总括来看,他们的关系如下图: (对了,题外话:Bitcoin用的是JSON-RPC

RPC关系图

为什么要用gRPC

OK,上面讲了各种概念。那么,为什么要用gRPC呢? (注意,我这里的问题是「为什么要用gRPC」,而不是「为什么要用RPC」)

天下武功,唯快不破

这是一条受用千年的古训。

gRPC用了上面提到过的Protocol Buffers,在数据传输过程,数据包/payload是基于二进制/binary的。 所以,数据包的size,比JSON小很多(想象一个例子:一个55bytes,一个20bytes)。 另外,二进制形式的数据包,CPU可以更高效地进行「序列化」和「反序列化」。

所以,概括来说,用gRPC的小伙伴,是想榨出更多的性能。

当然,gRPC也不是万金油,也有自己的劣势:浏览器支持有限、二进制格式对人类不友好等等。

更多的优劣分析,可以参考: What is gRPC: Main Concepts, Pros and Cons, Use Cases

至于你要不要用gRPC,请自行斟酌——跟我好像没有关系。

iOS App端如何实现和RPC服务器通信

好了,上面讲了一大堆屁话,终于到正题了。

要写一个iOS的App,和gRPC后台通信。首先,我们要有一个gRPC后台——好一句废话。

服务端跑起来

没有后台经验的小伙伴不需要菊花一紧,你只需要在你的终端敲入swift run HelloWorldServer这行命令,然后再轻轻敲一下回车键,官方GitHub的HelloWorld后台,就会神奇般地跑起来了:

  • grpc-swift项目clon下来
  • cd到项目根目录
  • 打开终端/Termanil,执行swift run HelloWorldServer命令(成功后会看到终端的打印:server started on port 1234

这样,RPC后台就跑起来了。从这个后台能拿到什么数据? 首先这个后台有一个方法sayHello()可供(App)客户端调用,然后,假如你调用这个方法并传入Antony作为方法的参数(准确说应该是一个Rquest对象),他会返回字符串Hello Antony!(准确说应该是一个Response对象)。如果不传参数,默认返回Hello stranger!。 有没有很厉害?!

如果你迫不及待,没写好App,就想调sayHello()方法试试看。可以:

  • 再打开一个终端
  • cd到项目根目录
  • 执行swift run HelloWorldClient命令(成功后会看到打印:Greeter received: Hello stranger!) 表示我们的客户端(是一个命令行工具)调用了sayHello()并收到了后台服务端的数据了!

RPC后台跑起来!

.proto文件的撰写

在写App之前,还想介绍一下 .proto文件。

上面介绍了,我们客户端这边,调用了sayHello()方法,同样地,到时候我们的App,也会调用这个方法,获取数据,而这个方法自然是用Swift语言写的,我们需要自己写这个方法吗?答案是不需要。那这个方法从哪里来?

答案就是接下来介绍的 .proto文件。我们利用Protocol Buffers这个接口描述语言,来把我们的数据传输过程中的「数据模型」和「方法」在 .proto文件定义好,然后再通过相关指令,生成你的客户端需要的代码。比如iOS的Swift、Android的Kotlin等等。 (上面说过的「gRPC支持多种语言」,就是这个意思。)

下面是仓库中的helloworld.proto 文件

代码语言:javascript
复制
// Protocol Buffers有proto2版本,这里表明,我们用的是比较新的proto3版本
syntax = "proto3";

// 下面的option,是生成代码时候的一些配置
option java_multiple_files = true; // 生成的Java代码,是否分成多个文件
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW"; // 生成的Objective-C代码的前缀是什么

// 「包名」。想象一下,你在这里定义、最后生成的「类」和「方法」,有可能会和你原来App的「类」、「方法」重名。
// 这里加一个package的名称,避免「命名冲突」
package helloworld;

// 定义一个service
// 事实上你可以在同一个 .proto文件,定义多个serive(按我目前理解,这样做可以让不同功能的APIs,组织得更有条理?)
service Greeter {
  // SayHello接口方法的具体定义
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// 参数HelloRequest的定义
// 注意,这里的1,并不是给name赋值,而是标记tag,用于序列化和反序列化时的字段匹配
// 这里的message关键字,可以理解成和class类似
message HelloRequest {
  string name = 1;
}

// 返回值类型HelloReply的定义
message HelloReply {
  string message = 1;
}

// 如果有其他的数据模型和方法,继续添加就好。是不是挺简单的?

具体的语法介绍: Language Guide (proto3)

这里需要说明一下, .proto文件,理论上是负责后台的工程师去撰写的。 可能比较nice一点的同事,会顺便生成swift文件给你,你直接用就可以了。没那么nice的,可能会把 .proto文件丢给你,让你自己玩。

不过这里的最佳实践,我相信是前后端的工程师一起讨论 .proto文件中API接口的撰写,毕竟前后端开发有差异,很难避免写出一些不符合对方预期的API接口。

有兴趣的前端小伙伴,也可以试试往helloworld.proto 文件加点方法,改点内容,重新生成代码,更新实现。感受一下后台的开发。

接口代码的生成

OK,现在我们有 .proto文件了,假如我们碰到一位没那么nice的后台同事,把 .proto文件直接丢过来,要怎么生成Swift代码?

gRPC Swift 提供了一个插件/plugin,叫protoc(名字算是起得够烂了?让人很confusing)。详见: protoc gRPC Swift plugin (如果没有安装这个插件而运行生成代码的指令,报错command not found: protoc

插件的安装,如果是macOS(应该没有人用Windows做iOS开发的?),直接在终端执行命令brew install swift-protobuf grpc-swift,用Homebrew来安装。 更详细的安装说明:Getting the protoc Plugins

(这里有个坑,一开始我搜到的是gRPC官网的安装教程Protocol Buffer Compiler Installation,这个不是针对Swift的,安装后生成代码的时候会提示protoc-gen-grpc-swift: program not found or is not executable

装好后,就可以用命令来生成Swift代码了。不过,先看看生成的代码文件长什么样:

Generated Swift Files

可以看到,两个文件(命名还有点奇怪):

  • .grpc.swift文件生成的是:API接口方法(对应上面的SayHello方法)、Client(App端用到)、Provider(实现后台时用到——后台工程师用)
  • .pb.swift文件生成的是:模型类(对应上面的HelloRequestHelloReply

接着,就可以敲命令行生成代码了,个人感觉命令行还是有点复杂,敲敲打打半天,才搞明白,所以画图说明一下(以这个目录下的helloworld.proto文件为例。先cd到仓库的根目录grpc-swift):

代码生成指令说明

执行上面命令后,如无意外,就会得到helloworld.grpc.swifthelloworld.pb.swift两个文件。

可参考: protoc gRPC Swift plugin——不过感觉还没我讲得清楚

App端请求数据

终于可以写App端的代码了!!!

新建一个iOS工程,获取gRPC Swift:可以用Swift Package Manager;可以手动导入;也可以用CocoaPods。详情可以看Github仓库的README

连接服务器,调用方法,获取数据

接着可以连接gRPC服务器了并获取数据了:

代码语言:javascript
复制
let group = PlatformSupport.makeEventLoopGroup(loopCount: 1)

// 创建一个channel
// 通过host和port,就知道连接那个服务器了
let channel = try? GRPCChannelPool.with(target: .host("localhost", port: 1234),
                                        transportSecurity: .plaintext,
                                        eventLoopGroup: group)

// 创建Client对象
// Helloworld_GreeterClient是根据.proto文件生成的代码
let greeter = Helloworld_GreeterClient(channel: channel!)

// 创建Request对象,作为方法的参数传给服务器
let request = Helloworld_HelloRequest.with {
    $0.name = "ANTONY"
}

// 传入参数,调用方法
let sayHello = greeter.sayHello(request)

do {
    // 拿到方法的返回值(后台返回的数据)
    let response = try sayHello.response.wait()
    print("Greeter received: \(response.message)")
} catch {
    print("Greeter failed: \(error)")
}

最后会看到Xcode控制台打印:Greeter received: Hello ANTONY!。这样就完成gRPC「客户端」和「服务器」之间的数据传输了。

Are you kidding me? 就这几行代码?你写了3000字?

OK,别着急,后面再写进阶一点的内容。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-06-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是RPC、gRPC、grpc-swift
    • What is RPC
      • What is gRPC
        • What is grpc-swift
        • 为什么要用gRPC
        • iOS App端如何实现和RPC服务器通信
          • 服务端跑起来
            • .proto文件的撰写
              • 接口代码的生成
                • App端请求数据
                  • 连接服务器,调用方法,获取数据
              相关产品与服务
              命令行工具
              腾讯云命令行工具 TCCLI 是管理腾讯云资源的统一工具。使用腾讯云命令行工具,您可以快速调用腾讯云 API 来管理您的腾讯云资源。此外,您还可以基于腾讯云的命令行工具来做自动化和脚本处理,以更多样的方式进行组合和重用。
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档