RPC协议之争和选型要点

《Netty 进阶之路》、《分布式服务框架原理与实践》作者李林锋深入剖析RPC协议之争和选型问题。李林锋此后还将在 InfoQ 上开设 Netty 专题持续出稿,感兴趣的同学可以持续关注。

1. 协议之争背景

1.1 RPC调用的协议选择

RPC调用的协议选择包含两部分:

1.协议栈:广义上协议栈可以分为公有协议和私有协议,例如HTTP、SMPP、WebService等都是公有协议;如果是某个公司或者组织内部自定义、自己使用的协议,没有被国际标准化组织接纳和认可的,往往划为私有协议,例如Thrift协议。

2.序列化方式:同一种协议也可以承载多种序列化方式,以HTTP协议为例,它可以承载文本类序列化方式,例如:XML、JSON等,也可以承载二进制序列化方式,例如谷歌的Protobuf。

不同的协议选择,对RPC调用的性能、开发难度和问题定位效率都有影响,因此,选择哪种协议,对RPC框架而言至关重要。由于各个协议都有自己的优缺点,因此很多框架在技术选型时都非常纠结。各种观点存在激烈交锋,有的看重性能和时延、有的更看重跨语言和可维护性。协议的选择是RPC框架构建的一个技术难点。

1.2 私有协议流行的原因

私有协议本质上是厂商内部发展和采用的标准,除非授权,其他厂商一般无权使用该协议。私有协议也称非标准协议,就是未经国际或国家标准化组织采纳或批准,由某个企业自己制订,协议实现细节不愿公开,只在企业自己生产的设备之间使用的协议。私有协议具有封闭性、垄断性、排他性等特点。如果网上大量存在私有(非标准)协议,现行网络或用户一旦使用了它,后进入的厂家设备就必须跟着使用这种非标准协议,才能够互连互通,否则根本不可能进入现行网络。这样,使用非标准协议的厂家就实现了垄断市场的愿望。

尽管私有协议具有垄断性的特征,但并非所有的私有协议设计者的初衷就是为了垄断。由于现代软件系统的复杂性,一个大型软件系统往往会被人为地拆分成多个模块,另外随着移动互联网的兴起,网站的规模也越来越大,业务的功能越来越多,为了能够支撑业务的发展,往往需要集群和分布式部署,这样,各个模块之间就要进行跨节点通信。

在传统的Java应用中,通常使用以下4种方式进行跨节点通信。

1.通过RMI进行远程服务调用。

2.通过Java的Socket+Java序列化的方式进行跨节点调用。

3.利用一些开源的RPC框架进行远程服务调用,例如Facebook的Thrift,Google的gRPC等。

4.利用标准的公有协议进行跨节点服务调用,例如HTTP+XML、Restful+JSON或者WebService。

跨节点的远程服务调用,除了链路层的物理连接外,还需要对请求和响应消息进行编解码。在请求和应答消息本身以外,也需要携带一些其他控制和管理类指令,例如链路建立的握手请求和响应消息、链路检测的心跳消息等。当这些功能组合到一起之后,就会形成私有协议。

私有协议的优点:灵活性高,可以按照业务的使用场景来设计和优化,在某个公司或者组织内部使用时也可以按需定制和演进,所以大部分RPC框架都支持私有二进制协议,例如阿里的Dubbo、华为的ServiceComb、Apache的Thrift等。

1.3 序列化方式

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

Java序列化从JDK 1.1版本就已经提供,它不需要添加额外的类库,只需实现java.io.Serializable并生成序列ID即可,因此,它从诞生之初就得到了广泛的应用。

但是在远程服务调用(RPC)时,很少直接使用Java序列化进行消息的编解码和传输,这又是什么原因呢?下面通过分析Java序列化的缺点来找出答案:

缺点1:无法跨语言,是Java序列化最致命的问题。对于跨进程的服务调用,服务提供者可能会使用C++或者其他语言开发,当我们需要和异构语言进程交互时,Java序列化就难以胜任。由于Java序列化技术是Java语言内部的私有协议,其它语言并不支持,对于用户来说它完全是黑盒。对于Java序列化后的字节数组,别的语言无法进行反序列化,这就严重阻碍了它的应用。事实上,目前几乎所有流行的Java RCP通信框架,都没有使用Java序列化作为编解码框架,原因就在于它无法跨语言,而这些RPC框架往往需要支持跨语言调用。

缺点2:相比于业界的一些序列化框架,Java默认的序列化效能较低,主要体现在:序列化之后的字节数组体积较大,性能较低。在同等情况下,编码后的字节数组越大,存储的时候就越占空间,存储的硬件成本就越高,并且在网络传输时更占带宽,导致系统的吞吐量降低。Java序列化后的码流偏大也一直被业界所诟病,导致它的应用范围受到了很大限制。

当前比较流行的序列化方式可以分为两大类:

1.文本类序列化方式:主要包括JSON和XML,它们的优点是:支持跨语言、可读性好、配套的支持工具比较全。缺点就是:序列化之后的码流比较大、冗余内容多,性能相对比较差。

2.私有的二进制类序列化方式:比较流行的有Thrift序列化框架、MessagePack和谷歌的Protobuf框架。它的优点是性能高,缺点就是可读性差,支撑的工具链不健全。

2. RPC协议选型要点

2.1 协议栈的选择

2.1.1 公有协议

尽管公有协议种类繁多,例如之前非常流行的WebService、WADL等,但目前来看,如果选择公有协议,HTTP协议还是首选,具有Rest风格的Restful + JSON接口是当前最流行的方式。

RPC协议如果选择Restful + JSON,会带来如下几个优点:

1.践行API First理念:通过使用Swagger YAML定义API,服务端和客户端都基于API定义,通过Swagger代码生成工具生成不同语言的接口和模型定义类库,客户端不需要从服务端导入接口定义类库,也不需要配置Maven依赖,这样就实现了双方依赖的解耦:

图1 基于Swagger代码工具生成服务端和客户端代码

除了代码生成,利用swagger editor和swagger ui工具,可以在线定义和维护API接口的契约,实现接口API的在线化管理,更好的管控API变更,示例如下:

图2 基于Swagger UI在线管理Restful API

对于RPC接口的测试,由于是Restful风格的,利用一些开源的契约测试框架,可以方便的进行契约化测试:

图3 对Restful风格的RPC API做契约化测试

2.天生语言中立:HTTP协议和JSON序列化天生就是语言中立的,对于RPC框架,在架构上能够支持多语言非常重要。不同的业务场景,适合不同的语言,例如后端复杂业务逻辑使用Java开发效率更高,对于API网关或者边缘服务,适合GO语言。对于一些序列化框架,由于使用了一些特定语言的特性,例如Exception、泛型等,这对于跨语言演进是个灾难,像protostuff就绑定了Java语言。

3.内部和外部API接口的统一:RPC服务通常只开放给内部的客户端做调用,如果需要开放给外部的端侧、其它渠道调用,往往需要前置一个API网关或者Edge Service,用来做安全接入、权限管理、统一流控、灰度发布等。组网示例如下所示:

图4 对RPC服务对外开放组网图

如果RPC调用选择Restful API,则对外开放时,API网关/Edge Service只要做安全等相关功能即可,消息可以透传转发给后端RPC服务。如果后端RPC服务选择私有协议,将私有协议直接开放给合作伙伴显然是不合适的,这就需要在API网关上为后端RPC服务定义API接口,同时做消息映射和转换,最终形成对外开放一套API接口,内部使用又一套API接口,但是功能却相同或者类似,这增加了接口的开发和维护工作量。

目前主流的API Gateway都支持直接导入Swagger定义的API,自动生成并发布API接口,以AWS的API Gateway为例,如下所示:

图5 AWS基于Swagger接口定义生成API(来自AWS官网示例教程)

当RPC服务开放给内部和外部是同一套API接口时,接口的开发和维护工作量都会减少很多。

4.问题定位更简单:HTTP协议 + JSON文本序列化方式,更容易调试,抓包的码流解读也更容易,相反如果是二进制私有协议,码流需要人工解读,难度较高。

2.1.2 私有协议

绝大多数的私有协议传输层都基于TCP/IP,对于Java语言,可以利用Netty的NIO TCP协议栈进行私有协议的定制和开发。

私有协议的格式往往存在较大差异,但是对于大多数RPC框架的私有协议,往往会包含如下几个字段:

1.消息头:消息头中通常会包含校验码、消息长度、消息类型、消息/会话ID、需要调用的RPC接口名、方法名,以及扩展的消息头,通常是个类似Map的结构,用于隐式传参。

2.消息体:对于请求消息,主要就是请求参数,对于响应,就是响应对象以及结果码。

下面以基于Netty开发的Netty协议栈为例进行说明,它的协议栈模型如下所示:

图6 Netty协议栈通信交互图

Netty协议栈承载了业务内部各模块之间的消息交互和RPC调用,它的主要功能包括:

1.基于Netty的NIO通信框架,提供高性能的异步通信能力。

2.提供消息的编解码框架,可以实现POJO的序列化和反序列化。

3.提供基于IP地址的白名单接入认证机制。

4.链路的有效性校验机制。

5.链路的断连重连机制。

RPC协议栈交互的流程说明如下:

1.Netty协议栈客户端发送握手请求消息,携带节点ID等有效身份认证信息。

2.Netty协议栈服务端对握手请求消息进行合法性校验,包括节点ID有效性校验、节点重复登录校验和IP地址合法性校验,校验通过后,返回登录成功的握手应答消息。

3.链路建立成功之后,客户端发送业务消息。

4.链路成功之后,服务端发送心跳消息。

5.链路建立成功之后,客户端发送心跳消息。

6.链路建立成功之后,服务端发送业务消息。

7.服务端退出时,服务端关闭连接,客户端感知对方关闭连接后,被动关闭客户端连接。

Netty协议栈的消息定义如下:

消息包含消息头和消息体:

名 称 类 型 长 度 描 述
header Header 变长 消息头定义
body Object 变长 对于请求消息,它是方法的参数(作为示例,只支持携带一个参数);对于响应消息,它是返回值

其中,消息头定义如下:

名 称 类 型 长 度 描 述
crcCode 整型 int 32 Netty消息的校验码,它由三部分组成: 1)0xABEF:固定值,表明该消息是Netty协议消息,2个字节; 2)主版本号:1~255,1个字节; 3)次版本号:1~255,1个字节。 crcCode = 0xABEF + 主版本号 + 次版本号

续表

名 称 类 型 长 度 描 述
length 整型 int 32 消息长度,整个消息,包括消息头和消息体
sessionID 长整型long 64 集群节点内全局唯一,由会话ID生成器生成
type Byte 8 0:业务请求消息; 1:业务响应消息; 2:业务ONE WAY消息(既是请求又是响应消息); 3:握手请求消息; 4:握手应答消息; 5:心跳请求消息; 6:心跳应答消息。
priority Byte 8 消息优先级:0~255
interfaceName String 变长
methodName String 变长
attachment Map<String, Object> 变长 可选字段,用于扩展消息头

对于私有协议栈的构建,需要考虑到如下几点,整体成本较高:

1.支持的数据结构类型,以及采用的序列化方式。

2.握手和接入认证。

3.心跳检测机制。

4.断连和重连机制。

5.并发连接数的控制。

6.异常、畸形码流的检测和保护。

7.流量限制和整形。

8.连接池。

9.网络闪断、宕机保护、消息缓存和重发机制等。

尽管私有协议栈构建成本较高,但是它的优势也很明显:

1.灵活性、可定制性更好,可以针对业务特定场景做优化。

2.可以实现更高的性能。

2.2 序列化框架

2.2.1 选型的原则

如果对性能要求不高,则建议选用JSON,否则可以选择一些业界主流的序列化框架,需要注意的是,对于序列化框架的选择,一定要考虑跨语言性,如果绑定特定语言,会对未来RPC框架支持多语言带来极大的困难。

2.2.2 Google的Protobuf

Protobuf全称Google Protocol Buffers,它由谷歌开源而来,在谷歌内部久经考验。它将数据结构以.proto文件进行描述,通过代码生成工具可以生成对应数据结构的POJO对象和Protobuf相关的方法和属性。

它的特点如下:

1.结构化数据存储格式(XML,JSON等)。

2.高效的编解码性能。

3.语言无关、平台无关、扩展性好。

4.官方支持Java、C++和Python三种语言(社区会支持更多中语言)。

Protobuf的优点主要有两个:

1.IDL契约:利用数据描述文件对数据结构进行说明,可以实现语言和平台无关,通过标识字段的顺序,可以实现协议的前向兼容,同时提供代码生成工具,可以生成各种语言的服务端和客户端代码。

2.性能:相比于其它序列化框架,它的性能更优,数据对比如下所示:

图7 Protobuf编解码和其他几种序列化框架的响应时间对比

2.2.3 Apache 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部分组成:

  1. 语言系统以及IDL编译器:负责由用户给定的IDL文件生成相应语言的接口代码;

  2. TProtocol:RPC的协议层,可以选择多种不同的对象序列化方式,如JSON和Binary;

  3. TTransport:RPC的传输层,同样可以选择不同的传输层实现,如socket、NIO、MemoryBuffer等;

  4. TProcessor:作为协议层和用户提供的服务实现之间的纽带,负责调用服务实现的接口;

  5. TServer:聚合TProtocol、TTransport和TProcessor等对象。

需要重点关注的是编解码框架,与之对应的就是TProtocol。由于Thrift的RPC服务调用和编解码框架绑定在一起,所以,通常我们使用Thrift的时候会采取RPC框架的方式。但是,它的TProtocol编解码框架还是可以以类库的方式独立使用的。

与Protobuf比较类似的是,Thrift通过IDL描述接口和数据结构定义,它支持8种Java基本类型、Map、Set和List,支持可选和必选定义,功能非常强大。因为可以定义数据结构中字段的顺序,所以它也可以支持协议的前向兼容。

Thrift支持三种比较典型的编解码方式。

  • 通用的二进制编解码

  • 压缩二进制编解码

  • 优化的可选字段压缩编解码

由于支持二进制压缩编解码,Thrift的编解码性能表现也相当优异,远远超过Java序列化和RMI等。

2.2.4 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));

2.2.5. 选型建议

上面列举的几种序列化框架各有优缺点,如果选用,则建议从如下几个维度做对比:

1.支持数据类型的丰富度。

2.性能对比测试,主要包括 序列化和反序列化的耗时、CPU和内存占用,以及序列化之后的码流大小。

3.尽管都支持跨语言,但是由于支持的语言丰富度不同,业务需要根据自己RPC框架未来可能支持的语言做选择。

3. RPC协议栈实践

3.1 Restful API的优化

使用Restful API可以带来很多收益:

  • API接口更加规范和标准,可以通过Swagger API规范来描述服务接口,并生成客户端和服务端代码。

  • Restful API可读性更好,也更容易维护。

  • 服务提供者和消费者基于API契约,双方可以解耦,不需要在客户端引入SDK和类库的直接依赖,未来的独立升级也更方便。

  • 内外可以使用同一套API,非常容易开放给外部或者合作伙伴使用,而不是对内和对外维护两套不同协议的API。

通常,对外开放的API使用Restful是通用的做法,但是在系统内部,例如商品中心和订单中心,RPC调用使用 Restful风格的API作为微服务的API,却可能存在性能风险。

3.1.1 HTTP1.X的性能问题

如果采用的Restful API 底层使用的HTTP协议栈是同步阻塞I/O,则服务端的处理性能将大打折扣:

1.性能问题:一连接一线程模型导致服务端的并发接入数和系统吞吐量受到极大限制。

2.可靠性问题:由于I/O操作采用同步阻塞模式,当网络拥塞或者通信对端处理缓慢会导致I/O线程被挂住,阻塞时间无法预测。

3.可维护性问题:I/O线程数无法有效控制、资源无法有效共享(多线程并发问题),系统可维护性差。

如果HTTP协议栈采用了异步非阻塞I/O模型(例如Netty、Servlet3.X版本),则可以解决同步阻塞I/O的问题,带来如下收益:

1.同一个I/O线程可以并行处理多个客户端链接,有效降低了I/O线程数量,提升了资源调度利用率

2.读写操作都是非阻塞的,不会因为对端处理慢、网络时延大等导致的I/O线程被阻塞

相比于TCP类协议,例如Thrift, 采用了非阻塞I/O的HTTP/1.X协议仍然存在性能问题,原因如下所示:

如果HTTP协议栈采用了异步非阻塞I/O模型(例如Netty、Servlet3.X版本),则可以解决同步阻塞I/O的问题,带来如下收益:

  • 同一个I/O线程可以并行处理多个客户端链接,有效降低了I/O线程数量,提升了资源调度利用率。

  • 读写操作都是非阻塞的,不会因为对端处理慢、网络时延大等导致的I/O线程被阻塞。

相比于TCP类协议,例如Thrift, 采用了非阻塞I/O的HTTP/1.X协议仍然存在性能问题,原因如下所示:

图8 HTTP/1.X 请求-响应模式

由于HTTP协议是无状态的,客户端发送请求之后,必须等待接收到服务端响应之后,才能继续发送请求(非websocket、pipeline等模式)。在某一个时刻,链路上只存在单向的消息流,实际上把TCP的双工变成了单工模式。如果服务端响应耗时较大,则单个HTTP链路的通信性能严重下降,只能通过不断的新建连接来提升I/O性能。但这也会带来很多副作用,例如句柄数的增加、I/O线程的负载加重等。显而易见,修8条单向车道的成本远远高于修一条双向8车道的成本。

除了无状态导致的链路传输性能差之外,HTTP/1.X还存在如下几个影响性能的问题:

  • HTTP客户端超时之后,由于协议是无状态的,客户端无法对请求和响应进行关联,只能关闭链路重连,反复的建链会增加成本开销和时延(如果客户端选择不关闭链路,继续发送新的请求,服务端可能会把上一条客户端认为超时的响应返回回去,也可能按照HTTP协议规范直接关闭链路,无路哪种处理,都会导致链路被关闭)。如果采用传统的RPC私有协议,请求和响应可以通过消息ID或者会话ID做关联,某条消息的超时并不需要关闭链路,只需要丢弃该消息重发即可。

  • HTTP本身包含文本类型的协议消息头,占用一些字节。另外,采用JSON类文本的序列化方式,报文相比于传统的私有RPC协议也大很多,降低了传输性能。

  • 服务端无法主动推送响应。

如果业务对性能和资源成本要求非常苛刻,在选择使用基于HTTP/1.X的Restful API 代替私有RPC API(通常是基于TCP的二进制私有协议)时就要三思;反之,如果业务对性能要求较低,或者在硬件成本和开放性、规范性上更看重后者,则使用Restful API也无妨。

3.1.2 优化方案

如果选择Restful API作为内部RPC或者微服务的接口协议,则建议使用HTTP/2.0协议来承载,它的优点如下:支持双向流、消息头压缩、单TCP的多路复用、服务端推送等特性,某个RPC调用超时也不需要关闭HTTP连接,只需要关闭对应的Stream流即可,这样可以避免大量超时时频繁的HTTP连接重建,有效解决传统HTTP/1.X协议遇到的问题,效果与RPC的TCP私有协议接近,采用HTTP/2的gRPC Steaming通信模式示例如下:

图9 gRPC基于HTTP/2的服务端streaming调用

3.2 Apache ServiceComb的多协议实践

3.2.1 标准化的服务契约

ServiceComb使用yaml文件格式定义服务契约,推荐使用Swagger Editor工具来编写契约,可检查语法格式及自动生成API文档。服务契约与具体协议无关系,无论使用RPC二进制协议通信还是使用标准的Restful + JSON,都可以使用Swagger API来描述和定义微服务接口。

接口定义完成之后,将契约文件放到"resources/microservices"或者"resources/application"目录下即可,目录结构如下所示:

图10 微服务接口契约定义

3.2.2 通信协议

ServiceComb实现了两种网络通道,包括Rest和Highway,均支持TLS加密传输。其中,REST网络通道将服务以标准Restful形式发布,调用端兼容直接使用Http client使用标准Restful形式进行调用。

Rest协议栈支持两种实现方式:

1.REST over Servlet对应使用web容器部署运行,需要新建一个servlet工程将微服务包装起来,打成war包,加载到web容器中启动运行。

2.REST over Vertx通信通道对应使用standalone部署运行模式,可直接通过main函数拉起。使用REST over Vertx网络通道需要在maven pom文件中添加如下依赖:

图11 REST over Vertx通信方式

除了对Rest的支持,在一些需要高性能、低时延的场景,使用私有二进制性能会更高一些。Highway是ServiceComb的高性能私有协议,用户可在有特殊性能需求的场景下选用。

使用Highway网络通道需要在maven pom文件中添加如下依赖:

图12 Highway通信方式

ServiceComb的通信协议有如下几个特点:

1.协议与服务接口契约定义没关系,使用Swagger定义的API接口,仍然可以使用二进制Highway私有协议进行RPC调用。

2.协议与业务代码无耦合关系,一套业务接口,可以选择发布成哪种协议。支持同一个服务接口发布成多种协议,客户端可以根据自己的需要选择哪种协议方式调用。

3.2.3 对HTTP/2协议的支持

ServiceComb提供了对HTTP/2的支持,用于解决传统HTTP/1.1的性能问题,它支持2种HTTP/2通信方式:

1.h2(Http2 + TLS):服务端在配置服务监听地址时,可以通过在地址后面追加?sslEnabled=true开启TLS通信,示例如下:

图13 h2通信方式配置

2.h2c(Http2 without TLS):服务端在配置服务监听地址时,可以通过在地址后面追加?protocol=http2启用h2c通信:

图14 h2c通信方式配置

3.2.4.性能对比数据

Restful和Highway(私有二进制协议)两种协议在不同模式的性能对比数据如下所示:

图15 性能对比数据

通过上述性能测试数据对比可以发现,私有的二进制协议Highway(TCP + Protobuf)比Restful+JSON协议整体性能高2倍+左右。如果Restful+JSON采用HTTP/1.1承载,无法实现链路的多路复用,一旦RPC调用超时就会频繁重建链路,可靠性相比于TCP私有协议会差很多。

4. 总结

4.1.协议与接口的解耦

无论是选择Restful(HTTP协议)还是私有二进制协议,总是存在各自的优缺点,对于比较复杂的业务场景,可能多种协议都需要支持,这就要求RPC框架必须实现接口定义与协议本身的解耦,即业务可以平滑的切换协议,上层应用不需要感知。

4.2 RPC协议的定制和扩展

一个比较好的协议往往具备较强的扩展性,协议的扩展主要体现在两点:

1.协议栈本身的扩展,例如基于协议框架扩展出另一种协议。

2.协议栈采用的序列化框架的扩展。

对于RPC框架而言,更有价值的是序列化框架的扩展,可以在协议头中增加一个标识协议类型的字段,RPC框架根据协议类型来调用对应的序列化框架,实现业务自定义的序列化和反序列化。

4.3 一些技术难点

不同的序列化框架,对数据类型的支持不同,对字段是否支持乱序也存在差异,所以,从某种程度看,协议和序列化方式完全与业务接口解耦也是很困难的,会有很多约束和限制,所以需要辨证的看待接口和协议的解耦,尽量做到对业务的影响最小、以及约束和限制的规范化。

5. 作者简介

李林锋,10年Java NIO、平台中间件设计和开发经验,精通Netty、Mina、分布式服务框架、API Gateway、PaaS等,《Netty进阶之路》、《分布式服务框架原理与实践》作者。目前在华为终端应用市场负责业务微服务化、云化、全球化等相关设计和开发工作。

联系方式:新浪微博 Nettying 微信:Nettying

Email:[email protected]

延伸阅读:

深入剖析通信层和 RPC 调用的异步化 (上)
深入剖析通信层和 RPC 调用的异步化 (下)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章