当下,基于“微服务”的技术架构体系几乎主宰了整个业务市场,尤其是在云原生生态的拥抱下。无论是基于传统虚拟机生态还是云原生容器生态的现代微服务体系结构中,我们可以根据微服务的交互及通信风格将其划分为两大类:面向外部的微服务和面向内部的微服务。
基于面向外部的微服务,其直接向使用者公开、暴露。此类主要是基于 HTTP 的 API,使用传统的基于文本的消息传递负载 ( JSON、XML等),这些负载针对外部开发人员进行了优化,并使用具有抽象状态传输 ( Representational State Transfer, REST ) 作为事实上的通信技术。
然而,与此同时,在实际的业务场景中,我们也迫切需要这样一种诉求,即微服务没必要与外部系统或外部开发人员建立通信。这些所谓的微服务能够在所设立的特定空间内相互交互以完成一组给定的任务。针对所述的服务,我们姑且称之为“面向内部微服务”,其主要基于同步或异步通信以完成所承载的业务诉求。
基于大多数的业务场景实现,我们可以看到在 HTTP 上使用 REST API 作为同步模式的身影,然而,随着业务架构的不断演进,此种策略并不是最优的解决方案。基于此,在本文中,我们将尝试进引入二进制协议,比如 gRPC,作为优化的通信协议,用于服务间通信,并对其进行简要剖析,以使得大家能够对其基本原理及应用场景有所了解。
何为 gRPC ?
gRPC ,全称为 “Google Remote Procedure Call,Google 远程过程调用”,是Google 发布的基于 HTTP 2.0 传输层协议承载的高性能开源软件框架,提供了支持多种编程语言的、对网络设备进行配置和管理的方法。其主要用于服务之间的高性能通信。
在应用层级,gRPC 可简化客户端与后端服务之间的信息传递。从 Google 开始,gRPC 是开放原始码和云端原生运算基础的一部分,CNCF 云端原生供应项目的生态系统。在云端原生应用程序中,开发人员通常可以跨程序设计语言、构架和技术。这种互通性会使信息合约和跨平台通信所需的管道变得更复杂。gRPC 会提供“统一水准图层“,以摘要说明这些考虑。使得开发人员在其原生平台中撰写的代码着重于逻辑功能实现,而借助 gRPC 处理通信管道。
基于微服务体系结构所搭建的业务系统,其中主要好处之一便是通过使用最合适的编程语言构建不同的服务,而不是拘泥于一种语言来构建所有内容。以 gRPC 为例,其由 Google 开发并开源的一种语言中立的 RPC 框架,当前支持 C、Java 和 Go 语言,其中 C 版本支持 C、C++、Node.js、C# 等等,基于我们的业务特性,能够进行自适应性设计及开发。下图为一个简要的电商系统架构拓扑,具体如下所示:
那么,相比之前的 RPC 框架,gRPC 具有哪些特点呢?
RPC (Remote Procedure Call),又称远程过程调用,是一种通过掩藏底层网络通信复杂性,从而屏蔽远程和本地调用区别的通信方式。相比于 Http 协议,RPC 协议属于一种自定义的 TCP 协议,从而在实现时避免了一些 Http 协议信息的臃肿问题,实现了更高效率的通信。针对 RPC ,可认为其是一种比 REST 更古老的协议,基于 API 的现代应用程序中以不同的方式用于实施部署。其 API 是通过定义公共方法来构建的,然后采用参数调用的方法。RPC只是一堆函数,但是在 HTTP API 上下文中,它需要将方法放到 URL 中,并将参数放到查询字符串或主体中。RPC API 使用类似于 POST /deleteResource 的方法,它的主体是{“id”:1},而不是 REST 方法,后者是DELETE /resource/1。
gRPC 是在 RPC 协议上创建的最新框架。它利用自身的优势,试图解决传统 RPC 存在的一系列问题。具体如下所示:
1、gRPC 基于 Protocol Buffer ,简称 “PB”,作为序列化和通信的接口定义语言,而并非是传统的JSON/XML。(这也是两者最为本质的区别)
Protocol Buffer 可以描述数据的结构,并且可以根据该描述生成代码,以生成或解析表示结构化数据的字节流。这就是为什么 gRPC 更适合使用 Polyglot(使用不同的技术部署)的 Web 应用程序。二进制数据格式使得通信更加轻量,gRPC 也可以与其他数据格式一起使用,但首选的格式仍然是 Protocol Buffer。
2、gRPC 构建在 HTTP/2 之上,其支持双向通信以及传统的请求/响应。gRPC 允许服务器和客户端之间的松散耦合。在实践中,客户端发起一个与 gRPC 服务器的长连接,并为每个 RPC 调用打开一个新的 HTTP/2 流。
基于上述所述,我们可以看到 gRPC 作为一种新兴的框架,其具备以下特点:
1、支持多种语言交互
2、通信协议基于标准的 HTTP/2 设计,支持双向流、消息头压缩、单 TCP 的多路复用、服务端推送等特性,这些特性使得 gRPC 在移动端设备上更加省电和节省网络流量
3、序列化支持 PB(Protocol Buffer) 和 JSON,PB 是一种语言无关的高性能序列化框架,基于 HTTP/2 + PB,保障了 RPC 调用的高性能
4、基于 IDL 文件定义服务,通过 proto3 工具生成指定语言的数据结构、服务端接口以及客户端 Stub
接下来,我们来了解下 gRPC的相关网络结构,首先,先看一下其网络架构及工作机制,gRPC 网络采用客户端/服务器模型,使用 HTTP 2.0 协议传输报文,具体如下所示:
上图简要描述了gRPC 网络的相关工作机制及活动流程。我们来看下 gRPC 协议栈,具体如下所示:
在上面章节,我们讲到了 gRPC 与 RPC 框架最为本质的区别在于 “Protocol Buffer” 协议。那么,什么是 ProtocolBuf 协议?
关于 gRPC 的 Protocol Buffers,其本质是 Google 的一种数据交换的格式,独立于语言和平台。Google 提供了多种语言的实现及支持:JAVA、C++ 以及 Python,每一种实现都包含了相应语言的编译器以及库文件。作为一种二进制的格式,比使用 Xml 进行数据交换要快很多,从而提升性能。除此,PB 也可以用于分布式应用之间的数据通信或者异构环境下的数据交换。作为一种效率及兼容性都非常出色的二进制数据传输格式,其广泛应用于诸如网络传输、配置文件、数据存储等诸多领域。
Proto文件定义了协议数据中的实体结构 (message, field),具体如下所示:
1、关键字 Message: 代表了实体结构,由多个消息字段 Field 组成
2、消息字段 Field: 包括数据类型、字段名、字段规则、字段唯一标识、默认值
我们以某一电商交易系统订单微服务为例,简要描述下每个服务所定义的 Proto,具体如下所示:
syntax="proto3"; package eshop; service OrderService { rpc UpdateOrder(Item) returns (Order); } message Item { string itemNumber = 1; int32 quantity = 2; } message Order { string itemNumber = 1; int32 totalQuantity = 2; float subTotal = 3;
Protocol Buffers 编码提供了一种灵活、高效、自动序列化结构数据的机制。Protocol Buffers 与 XML、JSON 编码类似,但不同之处在于 Protocol Buffers 是一种二进制编码,其性能更高。 下面我们将 Protocol Buffers 和 JSON 编码格式进行对比解析,具体如下:
JSON 编码格式示例:
{ "deviceID": "10086", "deviceName": "APP", "deviceModel": "Apple IOS", "sensorPath": "Syslog/LogBuffer", "jsonData": { "notification": { "Syslog": { . } . } . }
与之对应的 Protocol Buffers编码格式示例:
{ 1:"10086", 2:"APP", 3:"Apple IOS", 4:"Syslog/LogBuffer", 5:"notification": { "Syslog": { . } . } . }
接下来,我们来了解下 ProtocolBuf 数据协议的优缺点,具体如下:
优点:
(1)二进制消息,无论是基于时间还是空间概念,其性能好、效率高
(2)平台无关,语言无关,可扩展
(3)提供了友好的动态库,使用简单
(4)解析速度快,比对应的XML快约20-100倍
(5)序列化数据非常简洁、紧凑,与 XML 相比,其序列化之后的数据量约为 1/3 到1/10
(6)支持向前兼容(新加字段采用默认值)和向后兼容(忽略新加字段),简化升级
(7)支持多种语言(可以把 proto 文件看做 IDL 文件)
(8)Netty 等一些框架集成
缺点:
(1)当前,官方仅支持C++,JAVA和Python语言绑定
(2)二进制可读性差、不具有自描述特性
(3)默认不具备动态特性(可以通过动态定义生成消息类型或者动态编译支持)
(4)只涉及序列化和反序列化技术,不涉及 RPC 功能(类似 XML 或者 JSON 的解析器)
前面,我们对比了 RPC 与 gRPC 框架的相关差异性,现在我们了解下 REST 与 gRPC 框架之间的差异性对比。针对 2者,RPC 和 REST 的定位不同,REST 面向资源,更注重接口的规范,因为要保证通用性更强,所以对外最好通过REST。而 RPC 面向方法,主要用于函数方法的调用,可以适合更复杂通信需求的场景。与通常使用 JSON 的REST 不同,gRPC 使用 Protocol Buffer,这是一种更好的数据编码方式。由于 JSON 是一种基于文本的格式,因此它比 Protobuf 格式的压缩数据要重得多。除此之外,与传统REST 相比,gRPC 的另一个重大改进是它使用 HTTP 2 作为其传输协议。REST 主要基于 HTTP 1.1 ,基本上是一个请求—响应模型。gRPC 利用了 HTTP2 的双向通信特性和传统的响应—请求结构。在 HTTP 1.1 中,当有多个请求来自多个客户端时,需要一个接一个提供服务,这很可能会使系统变慢。但,HTTP 2 允许多路复用,因此可以同时处理多个请求和响应。
基于以上所述,我们可以得出这样的一种结论:在常态化的 API 或大规模微服务通信的多语言通信场景下,gRPC 是一个非常不错的优先选择。
其实,基于已落地的业务场景,在主流实现 RPC 协议的框架中,比较著名、流行的有 Dubbo、Thrift 及 gRPC 等。毕竟,目前主流的容器发布平台 Kubernetes,以及 Service Mesh 开源平台 Istio 都是基于 gRPC 协议来实现内部组件之间的交互,因此,在 Service Mesh 微服务架构中,服务间通信采用 gRPC 协议,从某种角度上说会更具有原生优势。况且在此之前,gRPC 框架已经在分布式、多语言服务场景中得到了大量应用及落地,因此,可以预测在 Service Mesh 微服务架构场景下,基于 gRPC 框架的微服务通信方式会逐步成为主流,占据较多的市场份额。