2、架构-服务间的通信

        远程服务将计算机程序的工作范围从单机扩展至网络,从本地延 伸至远程,是构建分布式系统的首要基础。而远程服务又不仅仅是为 分布式系统服务的,在网络时代,浏览器、移动设备、桌面应用和服 务端的程序,普遍都有与其他设备交互的需求,所以今天已经很难找 到没有开发和使用过远程服务的程序员了,但是没有正确理解远程服 务的程序员却不少。

1、 远程服务调用

        远程服务调用(Remote Procedure Call,RPC)在计算机科学中 已经存在超过四十年时间,但在今天仍然可以在各种论坛、技术网站 上遇见“什么是RPC”“如何评价某某RPC技术”“RPC更好还是REST更 好”之类的问题,仍然有新的不同形状的RPC轮子被发明制造出来,仍 然有层出不穷的文章去比对Google gRPC、Facebook Thrift等各家的 RPC组件库的优劣。

2、进程间通信

尽管今天的大多数RPC技术已经不再追求这个目标了,但不可否 认,RPC出现的最初目的,就是为了让计算机能够与调用本地方法一样 去调用远程方法。

3、通信的成本

        1987年,在“透明的RPC调用”一度成为主流范式的时候,Andrew Tanenbaum教授曾发表论文“A Critique of The Remote Procedure Call Paradigm”[1],对这种透明的RPC范式提出一系列质问。

  • 两个进程通信,谁作为服务端,谁作为客户端?
  • 怎样进行异常处理?异常该如何让调用者获知?
  • 服务端出现多线程竞争之后怎么办?
  • 如何提高网络利用的效率?连接是否可被多个请求复用以减少 开销?是否支持多播?
  • 参数、返回值如何表示?应该有怎样的字节序?
  • 如何保证网络的可靠性?调用期间某个链接忽然断开了怎么 办?
  • 发送的请求服务端收不到回复怎么办?

论文的中心观点是:把本地调用与远程调用当作同样的调用来处 理,这是犯了方向性的错误,把系统间的调用透明化,反而会增加程 序员工作的复杂度。

最终,到1994年至1997年间,由ACM和Sun院士Peter Deutsch、套接字接口发明者Bill Joy、Java之父James Gosling等一 众在Sun公司工作的专家们共同总结了通过网络进行分布式运算的八宗 罪(8 Fallacies of Distributed Computing)[2]。

1)The network is reliable.——网络是可靠的。

2)Latency is zero.——延迟是不存在的。

’3)Bandwidth is infinite.——带宽是无限的。

4)The network is secure.——网络是安全的。

5)Topology doesn’t change.——拓扑结构是一成不变的。

6)There is one administrator.——总会有一个管理员。

7)Transport cost is zero.——不必考虑传输成本。

8)The network is homogeneous.——网络都是同质化的。

4、三个基本问题

1.如何表示数据

        这里的数据包括传递给方法的参数以及方法执行后的返回值。无 论是将参数传递给另外一个进程,还是从另外一个进程中取回执行结 果,都涉及数据表示问题。对于进程内的方法调用,使用程序语言预 置和程序员自定义的数据类型,就很容易解决数据表示问题;对于远 程方法调用,则完全可能面临交互双方各自使用不同程序语言的情 况,即使只支持一种程序语言的RPC协议,在不同硬件指令集、不同操 作系统下,同样的数据类型也完全可能有不一样的表现细节,譬如数 据宽度、字节序的差异等。有效的做法是将交互双方所涉及的数据转 换为某种事先约定好的中立数据流格式来进行传输,将数据流转换回 不同语言中对应的数据类型来使用。这个过程说起来拗口,但相信大 家一定很熟悉,就是序列化与反序列化。每种RPC协议都应该要有对应 的序列化协议。

2.如何传递数据

        如何传递数据,准确地说,是指如何通过网络,在两个服务的 Endpoint之间相互操作、交换数据。这里“交换数据”通常指的是应 用层协议,实际传输一般是基于TCP、UDP等标准的传输层协议来完成 的。两个服务交互不是只扔个序列化数据流来表示参数和结果就行, 许多在此之外的信息,譬如异常、超时、安全、认证、授权、事务 等,都可能产生双方需要交换信息的需求。

3.如何表示方法

        确定表示方法在本地方法调用中并不是太大的问题,编译器或者 解释器会根据语言规范,将调用的方法签名转换为进程空间中子过程 入口位置的指针。不过一旦要考虑不同语言,事情又立刻麻烦起来, 每种语言的方法签名都可能有差别,所以“如何表示同一个方法” “如何找到对应的方法”还是需要一个统一的跨语言的标准才行。这 个标准可以非常简单,譬如直接给程序的每个方法都规定一个唯一 的、在任何机器上都绝不重复的编号,调用时压根不管它是什么方 法、签名是如何定义的,直接传这个编号就能找到对应的方法。

5、分裂的RPC

        由于一直没有一个同时满足以上三点的“完美RPC协议”出现,所 以远程服务器调用这个小小的领域,逐渐进入群雄混战、百家争鸣的 战国时代,距离“统一”越来越远,并一直延续至今。现在,已经相 继出现过RMI(Sun/Oracle)、Thrift(Facebook/Apache)、 Dubbo(阿里巴巴/Apache)、gRPC(Google)、Motan1/2(新浪)、 Finagle(Twitter)、brpc(百度/Apache)、.NET Remoting(微 软)、Arvo(Hadoop)、JSON-RPC 2.0(公开规范,JSON-RPC工作 组)等难以穷举的协议和框架。

        这些RPC功能、特点不尽相同,有的是 某种语言私有,有的支持跨越多种语言,有的运行在应用层HTTP协议 之上,有的直接运行于传输层TCP/UDP协议之上,但并不存在哪一款是 “最完美的RPC”。今时今日,任何一款具有生命力的RPC框架,都不 再去追求大而全的“完美”,而是以某个具有针对性的特点作为主要 的发展方向,举例分析如下。

  • 朝着面向对象发展,不满足于RPC将面向过程的编码方式带到 分布式,希望在分布式系统中也能够进行跨进程的面向对象编程,代 表为RMI、.NET Remoting,之前的CORBA和DCOM也可以归入这类。 这种方式有一个别名叫作分布式对象(Distributed Object)。
  • 朝着性能发展,代表为gRPC和Thrift。决定RPC性能的主要因素 有两个:序列化效率和信息密度。序列化效率很好理解,序列化输出 结果的容量越小,速度越快,效率自然越高;信息密度则取决于协议 中有效负载(Payload)所占总传输数据的比例大小,使用传输协议的 层次越高,信息密度就越低,SOAP使用XML拙劣的性能表现就是前 车之鉴。gRPC和Thrift都有自己优秀的专有序列化器,而传输协议方 面,gRPC是基于HTTP/2的,支持多路复用和Header压缩,Thrift则直 接基于传输层的TCP协议来实现,省去了应用层协议的额外开销。
  • 朝着简化发展,代表为JSON-RPC,说要选功能最强、速度最快 的RPC可能会很有争议,但选功能弱的、速度慢的,JSON-RPC肯定会 是候选人之一。牺牲了功能和效率,换来的是协议的简单轻便,接口 与格式都更为通用,尤其适合用于Web浏览器这类一般不会有额外协 议支持、额外客户端支持的应用场合。

        经历了RPC框架的“战国时代”,开发者们终于认可了不同的RPC 框架所提供的特性或多或少是有矛盾的,很难有某一种框架能满足所 有需求。若要朝着面向对象发展,就注定不会太简单,如建Stub、 Skeleton就很烦了,即使由IDL生成也很麻烦;功能多起来,协议就会 更复杂,效率一般也会受影响;要简单易用,那很多事情就必须遵循 约定而不是自行配置;要重视效率,那就需要采用二进制的序列化器 和较底层的传输协议,支持的语言范围容易受限。也正是每一种RPC框 架都有不完美的地方,所以才导致不断有新的RPC轮子出现,也决定了 在选择框架时,在获得一些利益的同时,要付出另外一些代价。 到了最近几年,RPC框架有明显向更高层次(不仅仅负责调用远程 服务,还管理远程服务)与插件化方向发展的趋势,不再追求独立地 解决RPC的全部三个问题(表示数据、传递数据、表示方法),而是将 一部分功能设计成扩展点,让用户自己选择。框架聚焦于提供核心 的、更高层次的能力,譬如提供负载均衡、服务注册、可观察性等方 面的支持。这一类框架的代表有Facebook的Thrift与阿里的Dubbo,尤 其是断更多年后重启的Dubbo表现得更为明显。Dubbo默认有自己的传 输协议(Dubbo协议),同时也支持其他协议;默认采用Hessian 2作 为序列化器,如果你有JSON的需求,可以替换为Fastjson,如果你对 性能有更高的追求,可以替换为Kryo、FST、Protocol Buffers等效率 更好的序列化器,如果你不想依赖其他组件库,也可以直接使用JDK自 带的序列化器。这种设计在一定程度上缓和了RPC框架必须取舍、难以 完美的缺憾。 最后,笔者提个问题,大家不妨来反思一下:开发一个分布式系 统,是不是就一定要用RPC呢?RPC的三大问题源自于对本地方法调用 的类比模拟,如果我们把思维从“方法调用”的约束中挣脱,那在解 决参数与结果如何表示、数据如何传递、方法如何表示这些问题时都 会有焕然一新的视角。

6、REST设计风格

REST(Representational State Transfer)设计风格是由Roy Fielding在2000年他的博士论文中首次提出,用于指导网络应用的架构设计。REST是一种基于资源的架构风格,主要用于网络服务的设计和开发,它强调网络应用应当被视为资源的集合,并通过标准的HTTP方法进行操作。

REST的主要特征

  1. 资源导向:REST将应用中的各种内容视为资源,每个资源在网络上有唯一的URI(统一资源标识符)进行标识。
  2. 无状态操作:REST要求所有的请求从客户端到服务器都包含执行操作所需的所有信息,服务器不会保存任何客户端请求的状态信息,这使得REST服务易于扩展。
  3. 表现层状态转换:尽管REST服务是无状态的,但数据(资源)的表现形式可以根据需要在客户端和服务器之间转换,如XML、JSON等格式。
  4. 统一接口:REST使用统一的接口(比如HTTP的GET、POST、PUT、DELETE方法),使得与资源的交互变得标准化和简化。
  5. 可缓存的:REST允许响应数据被标记为可缓存的或不可缓存的。如果响应是可缓存的,客户端缓存机制可以根据需要重用响应数据。
  6. 按需代码(可选):服务器可以临时扩展或定制客户端的功能,通过传送可执行代码的方式(例如JavaScript),但这不是必需的。

REST与RPC的不同

REST和RPC(Remote Procedure Call)都是实现网络服务的流行方法,但它们在设计和使用上有显著的不同:

  1. 设计哲学

    • REST:基于资源的架构,每个URL代表一个资源,HTTP动词定义对资源的操作。它侧重于资源的状态和表现层的状态转换。
    • RPC:更像是在网络上调用方法。RPC隐藏了网络调用的复杂性,使远程服务器上的函数调用看起来像是本地调用。
  2. 通信方式

    • REST:使用HTTP标准方法(如GET、POST、PUT、DELETE)来执行对资源的操作。
    • RPC:可以使用HTTP POST或其他协议如gRPC(基于HTTP/2),并且通常调用特定的操作或服务方法。
  3. 用例适应性

    • REST:非常适合对资源进行标准操作的应用场景,如Web服务和公开API,其中资源是明确的,标准HTTP方法足以表达所有操作。
    • RPC:适用于需要明确操作和远程过程调用的场景,如内部服务通信,或者当操作不容易映射到标准HTTP方法时。
  4. 可缩放性和灵活性

    • REST:通过其无状态和可缓存的特性,具有较好的可扩展性和互联网级的缓存支持。
    • RPC:虽然可能不如REST缓存友好,但它可以提供更优化的性能和更紧密的服务集成。

总结来说,REST是一种更高层次的、基于资源的网络服务设计方法,强调资源的通用访问和操作的标准化。而RPC则侧重于效率和功能的直接调用,更适合对性能和紧密集成有较高要求的系统。选择哪种技术取决于具体的应用需求和场景。

  • 20
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值