API 安全策略和基础指南
初学者必看:RPC详解
远程过程调用(Remote Procedure Call,RPC)是一种允许两个实体通过通用请求/响应机制的通信通道进行通信的设计范例。RPC 的定义在过去三十年中发生了重大的变化和演变,因此 这里RPC 范式是一个广义的分类术语,指的是过去四十年中出现的所有 RPC 式系统。
RPC 的定义经过几十年的发展。它已经从一个简单的客户端-服务器设计转移到一组相互连接的服务。虽然最初的 RPC 实现被设计为将计算外包给分布式系统中的服务器的工具,但 RPC 经过多年的发展,已经构建了一个与语言无关的应用程序生态系统。RPC 范式已经成为创建真正革命性的分布式系统的驱动力的一部分,并且在不同系统之间产生了各种通信方案和协议。
RPC是API接口的技术部分,我们在讲API调用时,大部分情况其实是在讨论RPC实现相关的内容,理解RPC技术,对API是什么的认知非常重要,对于内部API、开放API的设计与技术选型也会有很大的帮助。
RPC发展历史的几个关键节点
- 奠基: 过程调用最早可以追溯到 Jon Postel 和 Jim White 在1974 年发表的 Procedure Call Protocol Documents Version 2(RFC674)。这个协议试图定义一种通用的方法,用于解决 NSW 项目中多个计算节点通信的问题。
- 第一个RPC标准: 1988年,RFC 1057 发布,ONC RPC(Sun RPC) 被定义为标准的RPC规范。ONC RPC 提供了一个编译器,需要一个远程过程接口的定义来生成客户端和服务器的存根函数。这个编译器叫做 rpcgen。在运行此编译器之前,程序员必须提供接口定义,包含函数声明的接口定义,通过版本号进行分组,并被一个独特的程序编码来标识。这一过程已经成为现在RPC的基本调用模型。
- 第一个对象模型调用标准: 1991年,OMG 发布CORBA 1.0,试图为应用程序开发人员提供几个好处: 语言独立性、操作系统独立性、体系结构独立性、通过 IDL 中的抽象类型映射到这些类型的机器和语言特定实现的静态类型,以及对象传输,其中对象可以通过不同机器之间的连接进行迁移。
- HTTP诞生: 1996年:HTTP/1.x 版本发布,为互联网的发展奠定了基础,我们今天的大部分远程调用,都基于HTTP协议。
- REST概念提出: 2000年,Roy Thomas Fielding 博士在他的博士论文 《Architectural Styles and the Design of Network-based Software Architectures》首次提出了 REST 这个词。REST非常简单,无需客户端stub 代码 和服务端 stub代码,且所有语言都可以集成实现。HTTP REST慢慢侵占了RPC大部分应用领地的“异类”,并且导致了一度盛行的XML-RPC的灭绝,但同时促进了正统RPC技术走向一个新的发展阶段。
注:了解更详细的发展历史,请阅读RPC发展史。
RPC需要解决的问题
RPC 的出现的确为 分布式系统 构建带来了便利,但与此同时分布式系统本身的问题也暴露了出来:
- 延迟问题首先就是延迟的问题,最直观的表现就是响应时间变长:用户的一次点击事件可能需要经过多个服务处理,每个服务都被部署在不同的机器上,这种跨机器、跨网络的进程间通信更容易出现网络延迟。此外,数据编/解码带来的性能损耗也会拖慢响应速度。要解决这个问题,就得投入更大的网络带宽以及更强的硬件设备。
- 地址空间被隔离内存地址只在同一台机器上有效,在一台机器内,可以通过内存共享实现地址空间不被隔离,但是在跨机器场景下,地址空间是被完全隔离的。比如在使用指针时,本地地址空间的指针在另一台机器上是无法使用的,所以需要 RPC 通过编程范式对开发者隐藏这种区别。而作为开发者,也应该清楚 RPC 框架下的开发不可以直接使用原始指针。
- 局部故障在分布式架构中,不同的服务部署在不同的机器上,而且每个服务也会部署多个节点。当某服务的其中一个或几个节点发生故障时,若没有一个合适的发现机制,流量依旧请求到了故障节点,就会造成响应失败。为了解决这个问题,我们可以引入注册中心。注册中心可以发现并屏蔽掉发生故障的节点,但是注册中心也可能使故障类型变得模糊,定位问题的过程会更加复杂。除了发现问题、定位问题难度的上升,注册中心在解决局部故障上的难度也不小:因为出现局部故障后,需要保证整个集群的处理结果是一致的。比如需要通过分布式事务来保证整个集群所有节点写入数据是一致的,不会因为局部故障而出现故障节点写入数据失败但是非故障节点写入成功,导致数据不一致的情况。
- 并发问题在分布式架构中,每个服务都有多个节点,如果多个节点同时对某个服务发起调用,就会产生并发问题。与本地多线程调用不同,分布式架构无法做到完全控制调用顺序,因为每个节点在不同的机器上,它们发起调用的时间也难以被统一管控。
RPC的调用类型
在项目开发中常用的RPC调用方式有以下几种:
- Synchronous/同步模式,默认的RPC调用方式,客户端发起请求并等待服务端响应。
- Nonblocking/非阻塞模式,客户端发起请求后不等待服务端响应,继续执行自己的进程,该模式也可成为异步(Async)模式。
- Batch-mode/批量模式, 客户端发起一组非阻塞的调用。
- Broadcast/广播模式,客户端向多个服务器发送消息,然后接收所有结果回复。
- Callback/回调模式,配合非阻塞模式使用。
RPC的基本原理
RPC的核心思想是将远程调用封装成本地调用,使得调用方无需关心底层网络通信细节。当调用方需要调用远程函数时,它会像调用本地函数一样发起调用请求,然后等待远程函数的返回结果。
RPC调用流程
- 服务消费方(client)调用以本地调用方式调用服务;
- client stub接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体;
- client stub找到服务地址,并将消息发送到服务端;
- server stub收到消息后进行解码;
- server stub根据解码结果调用本地的服务;
- 本地服务执行并将结果返回给server stub;
- server stub将返回结果打包成消息并发送至消费方;
- client stub接收到消息,并进行解码;
- 服务消费方得到最终结果。
核心组件
RPC 技术发展至今,底层核心组成部分始终没有很大变化。无论是几十年前的 CORBA,还是如今流行的 Dubbo、gRPC 等,它们基本都由以下五个部分组成:
- Consumer(服务调用方);
- Consumer-Stub(调用方本地存根);
- RPC Runtime(RPC 运行时);
- Provider-Stub(提供方本地存根);
- Provider(服务提供方)。
序列化
什么是序列化?序列化就是将数据结构或对象转换成二进制串的过程,也就是编码的过程。
什么是反序列化?将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程。
为什么需要序列化?转换为二进制串后才好进行网络传输嘛!
为什么需要反序列化?将二进制转换为对象才好进行后续处理!
现如今序列化的方案越来越多,每种序列化方案都有优点和缺点,它们在设计之初有自己独特的应用场景,那到底选择哪种呢?从RPC的角度上看,主要看三点:
- 通用性:比如是否能支持Map等复杂的数据结构;
- 性能:包括时间复杂度和空间复杂度,由于RPC框架将会被公司几乎所有服务使用,如果序列化上能节约一点时间,对整个公司的收益都将非常可观,同理如果序列化上能节约一点内存,网络带宽也能省下不少;
- 可扩展性:对互联网公司而言,业务变化飞快,如果序列化协议具有良好的可扩展性,支持自动增加新的业务字段,而不影响老的服务,这将大大提供系统的灵活度。
目前互联网公司广泛使用Protobuf、Thrift、Avro等成熟的序列化解决方案来搭建RPC框架,这些都是久经考验的解决方案。
通讯协议
RPC调用过程通常采用的底层协议有:TCP、HTTP、UDP。报文格式由序列化方式来决定。
服务注册与服务发现
在分布式环境中,服务的命名及注册从约定模式逐步发展到了分布式注册中心,各种发现方式依然普遍应用于实现中,包括约定模式。
- 服务注册:服务进程在注册中心注册自己的元数据信息,通常包括主机和端口号等信息。
- 服务发现:客户端服务进程向注册中心发起查询,获取可用的服务信息。
RPC 技术和RPC 框架简介
常见 RPC 技术和框架有:
- 应用级的服务框架:阿里的 Dubbo/Dubbox、Google gRPC、Facebook Thrift、Spring Boot/Spring Cloud。
- 远程通信协议:Corba、SOAP API(HTTP+XML)、RESTful API(HTTP+JSON)。
- 通信框架:MINA 和 Netty。
- 各语言专用RPC实现框架:Java RMI、Golang Rpcx、C++ RCF、Python自带的RPC实现
RPC与SOAP
RPC 提供了一种简单且轻量级的通信协议,而 SOAP 提供了一个标准化的消息传递框架,可以跨不同的平台和编程语言使用。
- RPC(远程过程调用)是一种在远程系统上执行代码的协议,而 SOAP(简单对象访问协议)是一种基于 XML 的消息传递协议,用于交换数据。
- RPC 可以使用多种协议,包括 SOAP,而 SOAP 只依赖于 XML 和 HTTP。
- 与 RPC 相比,SOAP 提供了更好的互操作性和标准化,这可以简化跨各种平台的实施。
RPC与REST
REST和RPC协议具有相似的品质。例如,这两种协议都用于通过分布式系统进行通信。然而,它们仍然有不同的基本结构和用例。
REST必须是无状态的,但RPC系统可以是无状态的或无状态的。REST还具有更大的灵活性,因为它可以在一个API中以多种格式(如JavaScript对象符号或可扩展标记语言)发送数据,而RPC使用固定数据格式。