深入浅出gRPC
李林锋
《Netty 权威指南》、《分布式服务框架原理与实践》作者。
立即订阅
11252 人已学习
课程目录
已更新 6 讲 / 共 6 讲
01 | gRPC 入门及服务端创建和调用原理
02 | 客户端创建和调用原理
03 | gRPC 线程模型分析
04 | gRPC 服务调用原理
05 | gRPC 安全性设计
06 | gRPC 序列化机制
深入浅出gRPC
登录|注册

06 | gRPC 序列化机制

李林锋 2018-03-17

1. 常用的序列化框架

当进行远程跨进程服务调用时,需要把被传输的数据结构 / 对象序列化为字节数组或者 ByteBuffer。而当远程服务读取到 ByteBuffer 对象或者字节数组时,需要将其反序列化为原始的数据结构 / 对象。利用序列化框架可以实现上述转换工作。

1.1 Java 默认的序列化机制

Java 序列化从 JDK 1.1 版本就已经提供,它不需要添加额外的类库,只需实现 java.io.Serializable 并生成序列 ID 即可,因此,它从诞生之初就得到了广泛的应用。
但是在远程服务调用(RPC)时,很少直接使用 Java 序列化进行消息的编解码和传输,这又是什么原因呢?下面通过分析 Java 序列化的缺点来找出答案:
缺点 1:无法跨语言,是 Java 序列化最致命的问题。对于跨进程的服务调用,服务提供者可能会使用 C++ 或者其他语言开发,当我们需要和异构语言进程交互时,Java 序列化就难以胜任。
由于 Java 序列化技术是 Java 语言内部的私有协议,其它语言并不支持,对于用户来说它完全是黑盒。对于 Java 序列化后的字节数组,别的语言无法进行反序列化,这就严重阻碍了它的应用。
事实上,目前几乎所有流行的 Java RPC 通信框架,都没有使用 Java 序列化作为编解码框架,原因就在于它无法跨语言,而这些 RPC 框架往往需要支持跨语言调用。
缺点 2:相比于业界的一些序列化框架,Java 默认的序列化效能较低,主要体现在:序列化之后的字节数组体积较大,性能较低。
在同等情况下,编码后的字节数组越大,存储的时候就越占空间,存储的硬件成本就越高,并且在网络传输时更占带宽,导致系统的吞吐量降低。Java 序列化后的码流偏大也一直被业界所诟病,导致它的应用范围受到了很大限制。

1.2 Thrift 序列化框架

Thrift 源于 Facebook,在 2007 年 Facebook 将 Thrift 作为一个开源项目提交给 Apache 基金会。
对于当时的 Facebook 来说,创造 Thrift 是为了解决 Facebook 各系统间大数据量的传输通信以及系统之间语言环境不同需要跨平台的特性,因此 Thrift 可以支持多种程序语言,如 C++、Cocoa、Erlang、Haskell、Java、Ocami、Perl、PHP、Python、Ruby 和 Smalltalk。
在多种不同的语言之间通信,Thrift 可以作为高性能的通信中间件使用,它支持数据(对象)序列化和多种类型的 RPC 服务。
Thrift 适用于静态的数据交换,需要先确定好它的数据结构,当数据结构发生变化时,必须重新编辑 IDL 文件,生成代码和编译,这一点跟其他 IDL 工具相比可以视为是 Thrift 的弱项。
Thrift 适用于搭建大型数据交换及存储的通用工具,对于大型系统中的内部数据传输,相对于 JSON 和 XML 在性能和传输大小上都有明显的优势。
Thrift 主要由 5 部分组成:
语言系统以及 IDL 编译器:负责由用户给定的 IDL 文件生成相应语言的接口代码;
TProtocol:RPC 的协议层,可以选择多种不同的对象序列化方式,如 JSON 和 Binary;
TTransport:RPC 的传输层,同样可以选择不同的传输层实现,如 socket、NIO、MemoryBuffer 等;
TProcessor:作为协议层和用户提供的服务实现之间的纽带,负责调用服务实现的接口;
TServer:聚合 TProtocol、TTransport 和 TProcessor 等对象。
我们重点关注的是编解码框架,与之对应的就是 TProtocol。由于 Thrift 的 RPC 服务调用和编解码框架绑定在一起,所以,通常我们使用 Thrift 的时候会采取 RPC 框架的方式。
但是,它的 TProtocol 编解码框架还是可以以类库的方式独立使用的。
与 Protocol Buffers 比较类似的是,Thrift 通过 IDL 描述接口和数据结构定义,它支持 8 种 Java 基本类型、Map、Set 和 List,支持可选和必选定义,功能非常强大。因为可以定义数据结构中字段的顺序,所以它也可以支持协议的前向兼容。
Thrift 支持三种比较典型的编解码方式。
通用的二进制编解码;
压缩二进制编解码;
优化的可选字段压缩编解码。
由于支持二进制压缩编解码,Thrift 的编解码性能表现也相当优异,远远超过 Java 序列化和 RMI 等。

1.3 MessagePack 序列化框架

MessagePack 是一个高效的二进制序列化框架,它像 JSON 一样支持不同语言间的数据交换,但是它的性能更快,序列化之后的码流也更小。
MessagePack 提供了对多语言的支持,官方支持的语言如下:Java、Python、Ruby、Haskell、C#、OCaml、Lua、Go、C、C++ 等。
MessagePack 的 Java API 非常简单,如果使用 MessagePack 进行开发,只需要导入 MessagePack maven 依赖:
<dependency>
<groupId>org.msgpack</groupId>
<artifactId>msgpack</artifactId>
<version>${msgpack.version}</version>
</dependency>
它的 API 使用示例如下:
List<String> src = new ArrayList<String>();
src.add("msgpack");
src.add("kumofs");
src.add("viver");
MessagePack msgpack = new MessagePack();
byte[] raw = msgpack.write(src);
List<String> dst1 =
msgpack.read(raw, Templates.tList(Templates.TString));

1.4 Protocol Buffers 序列化框架

Google 的 Protocol Buffers 在业界非常流行,很多商业项目选择 Protocol Buffers 作为编解码框架,当前最新的为 Protocol Buffers v3 版本,它具有如下特点:
在谷歌内部长期使用,产品成熟度高;
跨语言、支持多种语言,包括 C++、Java 和 Python;
编码后的消息更小,更加有利于存储和传输;
编解码的性能非常高;
支持不同协议版本的前向兼容;
支持定义可选和必选字段。
Protocol Buffers 是一个灵活、高效、结构化的数据序列化框架,相比于 XML 等传统的序列化工具,它更小、更快、更简单。
Protocol Buffers 支持数据结构化一次可以到处使用,甚至跨语言使用,通过代码生成工具可以自动生成不同语言版本的源代码,甚至可以在使用不同版本的数据结构进程间进行数据传递,实现数据结构的前向兼容。

2. Protocol Buffers 介绍

区别于 Thrift,Protocol Buffers 是一个可独立使用的序列化框架,它并不与 gRPC 框架绑定,任何需要支持多语言的 RPC 框架都可以选择使用 Protocol Buffers 作为序列化框架。
Protocol Buffers 的使用主要包括:
IDL 文件定义(*.proto), 包含数据结构定义,以及可选的服务接口定义(gRPC);
各种语言的代码生成(含数据结构定义、以及序列化和反序列化接口);
使用 Protocol Buffers 的 API 进行序列化和反序列化。

2.1 支持的数据结构

Protocol Buffers 提供了对主流语言的常用数据结构的支持,考虑到跨语言特性,因此对于特定语言的特定数据结构并不提供支持,比较典型的如 Java 的 Exception 对象。

2.1.1 标量值类型(基本数据类型)

Protocol Buffers 支持的标量值类型如下:
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《深入浅出gRPC》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(7)

  • 广
    PB比较麻烦的是,如果协议有变动,需要重新生成

    作者回复: 是的,它是由契约生成代码的,如果你修改了代码,再生成原来的就会被覆盖掉,比较麻烦

    2018-08-21
    2
  • autorobert
    感谢楼主的文章,想了解下grpC的stream、server push、客户端管理这一块,比如Server怎么给几个根据特定条件筛选出来的client推送消息,谢谢。

    作者回复: 这个功能原生并没提供,需要自己通过路由扩展来实现。可以看下对接zookeeper做负载均衡的例子。另外,gRPC本质是RPC调用。如果是推送,实际上用的是底层HTTP/2的能力做扩展

    2018-03-21
    1
  • Baldwin
    想看看中间件的部分,发现没有
    2019-11-11
  • mhswordman
    楼主你好 proto中的message中如果在中间位置新增字段序列化的时候就报错这个可以讲一下嘛? 追加字段会不会报错
    2019-05-10
  • jack
    你好,grpc如何做流控,比如后端已经处理不了了,要拒绝掉新的请求
    2018-12-19
  • 刘金生
    文中对messagepack描述较少,看样子比proto更加灵活,请问这两个该如何选择?

    作者回复: 两者都是支持多语言的序列化框架,各自都有优缺点,建议可以从性能、业务集成角度考虑下

    2018-08-22
  • krugle
    grpc是不是不能像thrift一样生成客户端和服务端代码,百度很多都没有说明这个

    作者回复: 支持同时生成服务端和客户端的代码,你可以验证下

    2018-07-02
收起评论
7
返回
顶部