分布式服务框架(拾遗)
前言
现在的大部分工程都已经是基于分布式架构来处理。所以这里对分布式框架做一个简单的总结
常用的RPC框架
RPC框架原理
RPC(Remote Procedure Call,远程过程调用)一般用来实现部署在不同机器上的系统之间的方法调用,使得程序能够像访问本地系统资源一样,通过网络传输去访问远端系统资源。RPC框架实现的架构原理是类似的。
- Client Code :客户端调用方代码实现,负责发起RPC调用,为调用方用户提供使用API
- Serialization/Deserialization:负责对RPC调用通过网络传输的内容进行序列化和反序列化,不同的RPC框架有不同的实现机制。(后面章节有介绍序列化/反序列化)不同的序列化方式在可读性、码流大小、支持的数据类型及性能方面存在较大差异。
- Stub Proxy:可以看作是一种代理对象,屏蔽RPC调用过程中复杂的网络处理逻辑,使用RPC调用透明化,能够保持与本地调用代码一样的代码风格
- Transport:作为RPC框架底层的通信传输模块,一般通过Socket在客户端和服务端之间传递消息
- Server Code:服务端业务逻辑代码实现
RMI简介
Java RMI是一种基于Java的远程调用技术,是Java特有的一种RPC实现。其底层通信采用BIO实现的Socket完成、且使用了Java原生的序列化机制,所有序列化对象必须实现java.io.Serializable接口。
缺点
- Java原生序列化机制与BIO通信机制本身存在性能问题,导致RMI的性能较差,对性能要求高的使用场景不推荐使用该方案
CXF/Axis2简介
WebService是一种跨平台的RPC技术协议,WebService技术栈由SOAP(Sample Object Access Protocol,简单对象访问协议)、UDDI、WSDL组成。CXF/Axis2就是基于WebService实现的
Thrift介绍
Apache Thrift是跨越不同的平台和语言,协助构建可伸缩的分布式系统的一种RPC实现。最初由FaceBook内部开发使用,现在已经开源。最大的特点是具备广泛的语言支持,以及高性能。 Apache Thrift作为在多语言并存的异构系统之间的RPC调用方案是一个非常不错的选择。尤其对XML-PRC/JSON-RPC/SOAP-RPC与WSDL协议栈实现的RPC方案,有着非常明显的性能优势。原因在于Thrift是采用二进制编码协议、使用TCP/IP传输协议的一种RPC实现,而XML-PRC/JSON-RPC/SOAP-RPC与WSDL协议栈实现的RPC采用的是HTTP作为传输协议。 对于网络数据传输,TCP/IP协议的性能要远远高于HTTP协议,不仅是因为HTTP协议是应用层协议,HTTP协议传输内容除应用本身数据外,还带有不少描述本次请求上下文的数据(响应状态码、Header等)。此外HTTP协议一般使用文本传输协议对传输内容进行编码,相对于一般采用TCP/IP协议码流要大。
gRPC介绍
gRPC是Google的一个高性能、开源和通用的RPC框架,面向移动和HTTP/2设计。目前提供C、Java和Go语言版本。
HTTPClient介绍
超文本传输协议(HTTP)是现在互联网使用的最重要的协议。HTTPClient就是基于HTTP协议进行调用。这里不再赘述。
分布式服务框架总体架构与功能
面向服务的架构(SOA)是一种设计范式,用于创建解决方案的逻辑单元,这些逻辑单元可组合、可复用,以支持实现面向服务计算的特定战略目标和收益
服务拆分原则
目前在互联网企业,业务不断扩展、快速演进,单一的系统已经不足以承载大量的业务,如果把所有的业务实现在一个单一的系统应用中会产生以下问题:
- 业务模块边界不清,代码耦合严重,无法很好的实现代码模块级别或者功能级别的复用,从而服务快速的迭代
- 所有的开发人员都在一个应用工程代码库进行迭代开发、测试、发版,会导致应用发布上线过于频繁,不利于线上系统的稳定性。且整个应用代码的稳定性、可维护性很难得到保障
- 因为不同业务实现之间没有进行拆分隔离部署,某些高QPS耗时较长的复杂操作会影响整体应用的可用性。
解决以上问题的一个很好的做法就是系统拆分,以服务化的思想为指导,实现面向服务的架构,做服务化拆分。一般首先需要从整体上梳理公司的业务,将业务模块化,分解出各个业务模块之间的依赖及业务模块之间的边界。按照业务边界及业务之间的依赖顺序进行系统拆分,这是系统拆分的常见做饭。通过系统拆分实现SOA架构的价值,沉淀出一批稳定的后端服务,通过叠加复用快速响应用户的前端需求。
从单体到SOA架构的转变如下:
分布式服务总体架构
分布式服务框架主要包括服务消费端、服务提供端、服务数据网络传输协议的序列化/反序列化、服务数据的通信机制、服务注册中心、服务治理这几分部分组成
- 一般地,用Spring实现服务的引入与发布,分别实现服务消费端和服务提供端
- 服务数据的序列化/反序列化实现选择有多种,比如JSON格式、Thrift、Avro、Java默认序列化、XML格式等,
- 出于性能考虑,服务数据的通信机制目前的主流实现一般采用Netty、Mina或者类似的NIO网络通信框架
- 服务注册中心目前的主流是采用Zookeeper来实现,用来实现服务注册、服务发现、服务自动上下线等功能。也可以采用Netflix的Eureka来实现这些功能。
分布式服务架构由服务提供端、消费端、服务注册中心、服务治理四部分组成。
- 服务提供端启动服务,将服务提供者信息注册到服务注册中心,服务提供者信息一般包括服务主机地址、端口、服务接口信息等
- 服务消费端将服务提供者信息从服务注册中心获取到本地缓存,同时将服务消费者信息上报到服务注册中心
- 服务消费端根据某种软负载策略选择一个服务提供者发起服务调用,服务调用首先采用某种序列化方案,将数据序列化为可以在网络传输的字节数组,采用某种NIO框架完成调用
- 为了管理大规模的服务依赖关系,需要提供服务治理功能
序列化/反序列化实现
序列化(Serialization)是将对象的状态信息转化为可存储或者传输的形式过程,简言之,就是把对象转化为字节序列化的过程。反序列化则是序列化的逆过程。
评价一个序列化算法优劣的两个重要指标:
- 序列化后码流的大小
- 序列化本身的速度及系统资源开销大小(CPU、内存等)
Java默认序列化
Java默认序列化的优缺点都比较明显:
优点:
- Java语言自带,无须引用三方依赖
- 与Java语言有天然的最好的易用性和亲和性
缺点:
- 只支持Java语言,不支持跨语言
- Java默认的序列化性能欠佳,序列化后产生的码流过大,对于引用过程的对象序列化易发生OOM
XML序列化
XML序列化的优势在于可读性好,利于调试。但是由于使用标签来表示数据,导致序列化后码流大,而且效率不高。
JSON序列化
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式。相比XML,JSON序列化后码流小,可读性也好。目前主要的JSON序列化工具有:Jackson、fastJSON和GSON。相对而言Jackson和fastjson比GSON的性能好。Jackson和GSON比fastJSON稳定性高
Hessian序列化
Hessian是一个支持跨语言传输的二进制序列化协议
protobuf序列化
protobuf是Google的一种数据交换的格式,它独立于语言,独立于平台。是一个纯粹的展示层协议,可以和各种传输层协议一起使用。protobuf空间开销小及高解析性能,非常适合公司内部对性能要求高的RPC调用。 protobuf主要的问题是需要编写.proto IDL文件,使用起来工作量稍大,且需要额外学习proto IDL特有的语法。
Protostuff序列化
由于protobuf需要预先编写.proto IDL 文件,再通过protobuf提供的编译器生成对应于各种语言的代码。但是对于Java语言而言,Java具有反射和动态代码生成的能力,这个预编译过程不是必须的。仅对于Java语言,且无需跨语言的使用场景,Protostuff继承了Google的protobuf的高性能的同时免去了编写.proto 文件的麻烦。是一款值得推荐的序列化方式
Thrift序列化
thrift和protobuf类似,使用Thrift之前需要编写.thrift结尾的IDL文件,在使用Thrift提供的编译器编译成对应的代码。Thrift支持多种序列化协议。
Avro序列化
avro支持两种序列化编码方式,二进制编码和JSON编码,使用二进制编码的性能更高,序列化后产生的码流更小,而使用JSON编码的好处是编码后的可读性好。
JBoss Marshaling序列化
相对于原生Java序列化是一个很好的替代品
序列化框架的选型
由于每一种序列化协议都有自己的优缺点及适用场景。我们再选型的时候,一般从以下方面考虑: (技术层面)
- 序列化空间开销,即序列化后码流大小,码流过大会对带宽、存储空间造成较大压力
- 序列化时间开销,即序列化过程消耗的时长,序列化消耗时间过长会拖慢整个服务的响应时间
- 序列化协议是否支持跨平台、跨语言,公司内部是否存在异构系统通信需要
- 可扩展性/兼容性
- 成熟度及支持的数据结构的丰富性 (选型建议)
- 对于公司间的系统调用,性能要求在100ms以上的服务,基于XML的SOAP的协议是一个值得考虑的方案
- 基于Web Browser的Ajax以及Mobile APP与服务端之间的通信,JSON协议是首选
- 对于开发环境比较恶劣的考虑,采用JSON或者XML能够极大的提高调试效率
- 对于性能和简洁性有极高要求的场景,Hessian、protobuf、Thrift、Avro之间具有一定的竞争关系。
- 对于T级别的数据的持久化应用场景,protobuf和avro是首要选择
- 对需要提供一个完整的RPC解决方案,Thrift是一个好的选择
- 对序列化后需要支持不同的传输协议,或者需要跨防火墙访问的高性能场景,protobuf可以优先考虑
转载于:https://my.oschina.net/mrku/blog/1930260