老友记:华同学写了本 RPC 的书,小伙子 niubility ~
RPC作为目前的主流技术之一,它打破了某一项任务所需的计算资源只能靠一台计算机来实现的固有想法,对分布式计算、微服务等领域都有着重要而深远的影响。
从20世纪80年代至今近四十年的时间内,由RPC衍生出来的技术非常多,包括很多现在常见的中间件技术都离不开RPC。网络技术的发展,以及操作系统中的进程间通信技术越发多样化和成熟,这些都为RPC的出现打下了非常好的基础。
RPC框架是为了实现RPC而衍生出来的技术产物,它是RPC领域中可复用的软件架构解决方案。想要了解RPC框架原理,最重要的就是了解RPC框架怎么使用,了解RPC框架内对RPC技术的抽象,以及RPC框架提供的服务治理内容。
RPC(Remote Procedure Call)叫作远程过程调用,它是利用网络从远程计算机上请求服务,可以理解为把程序的一部分放到其他远程计算机上执行。通过网络通信将调用请求发送至远程计算机后,利用远程计算机的系统资源执行这部分程序,最终返回远程计算机上的执行结果。将“远程过程调用”概念分解为“远程过程”和“过程调用”来理解更加直观。
远程过程:远程过程是相对于本地过程而言的,本地过程也可以认为是本地函数调用,发起调用的方法和被调用的方法都在同一个地址空间或者内存空间内。而远程过程是指把进程内的部分程序逻辑放到其他机器上,也就是现在常说的业务拆解,让每个服务仅对单个业务负责,让每个服务具备独立的可扩展性、可升级性,易维护。在每台机器上提供的服务被称为远程过程,这个概念使正确地构建分布式计算更加容易,也为后续的服务化架构风格奠定了基础。
过程调用:这个概念非常通俗易懂,它包含我们平时见到的方法调用、函数调用,并且用于程序的控制和数据的传输。而当“过程调用”遇到“远程过程”时,意味着过程调用可以跨越机器、网络进行程序的控制和数据的传输。
举一个下订单的例子,下订单的步骤中必然有生成订单和支付订单这两个核心逻辑。
现在把生成订单的逻辑封装成订单服务部署到了机器A上,把支付订单的逻辑封装成支付服务部署到了机器B上。当用户下单时,下单的服务必然先调用A机器上的订单服务,获取A机器中返回的订单号、需要支付的金额等计算结果,然后将这些计算结果作为请求参数继续调用B机器上的支付服务,最终才能保证本次下单完成。其中的服务间调用就是RPC的理念。
RPC就好像是谈一场异地恋。两个服务之间的调用就像是一对分隔两地的情侣只能靠着手机来保持联系,远程调用中的协议就像是情侣间的暗语。当然,彼此也知道如何去编写消息和解析对方的消息,这也就是RPC中的编/解码。
RPC中的序列化方式就好比是女朋友让你往东,你千万不能往西,或者她给你一个眼神,是告诉你她想吃东西,你千万不能误会成不想吃。只有用相同的序列化方式,才不会导致服务调用出错。有关RPC框架的各类组成部分会在《深入理解RPC框架原理与实现》一书中的后续章节中介绍。
RPC是技术时代革新的产物,它是一个软件结构的概念。如果要说RPC能够带来什么价值,那么一定是它奠定了构建分布式应用的理论基础。
在互联网发展早期,人们对互联网依赖的程度并不高,一个网站或者一个应用的流量比较小,将所有功能都部署在一起,以便减少部署的成本和复杂度。
比如,网上购物中的浏览商品、下单、支付都在一个进程内完成,所有模块和代码都放在一起,前后端不分离,甚至数据库服务和应用服务被部署在同一个服务器上。
随着使用互联网产品的人群越来越庞大,单一应用架构在开发过程中随着系统应用越来越复杂,它所占用的资源也会越来越多,这个时候部署成本就会随之增加。而且这种架构导致多个功能模块糅杂在一起显得臃肿,线上质量也无法保证,慢慢地单一应用架构就失去了它唯一的优势,并且还暴露出容错性差、可扩展性差等一系列问题。
用户量增大,系统越来越复杂,单一应用架构已经不能满足需求,垂直应用架构随之出现。
垂直应用架构是指将数据与应用分离,将庞大臃肿的单一应用根据垂直业务的划分拆解成若干互不相干的应用以提升效率。
比如一个简单的商城系统,将商家的管理平台与买家的购物平台拆分分别部署。这种架构在一定程度上缓解了用户量增大带来的流量压力,只要哪个模块的流量压力大,就可以单独为这个模块水平扩容。
相对于单一应用架构,它有着更好的维护性、可扩展性,也方便协同开发。但是随着业务需求的增加,不同模块之间产生了业务交互的需求。比如订单模块希望访问用户模块,查询一些收货地址等信息。为了提高业务复用性,一些业务应用必须被拆解成子业务,这个时候分布式架构随之出现。分布式架构提升了服务的灵活性、可复用性,每个服务都可以弹性扩/缩容。
除此之外,分布式架构还实现了计算与存储的高可用性。而分布式架构最核心的就是利用RPC解决了服务之间的交互问题。所以RPC带来的效益也正是分布式架构带来的效益。
RPC的出现的确为构建分布式系统带来了便利,但是分布式系统本身的问题也被暴露出来。
第一个问题就是通信延迟的问题。
也就是我们经常提到的响应时间变长的问题。用户的一次点击事件可能需要经过多个服务处理,每个服务都被部署在不同的机器上,这种跨机器、网络进行进程间通信出现通信延迟情况的概率一定比同一台机器内的进程间通信更大。因为RPC依赖于互联网,可能出现网络延迟的情况。除了网络延迟,编/解码带来的性能损耗也是RPC相较于LPC(Local Procedure Call)的劣势。
有关性能的问题,早期的一些设想认为通过硬件和软件两方面可以一起解决这个问题,比如硬件的发展、虚拟内存技术等。随着网络技术的发展,网络通信速度不断提升,降低了RPC受网络延迟的负面影响,网络延迟也不再是RPC发展的障碍。除了RPC带来的延迟和性能问题,服务节点之间的网络通信也是不可靠的,可能出现乱序、内容错误、丢数据等问题。随着网络技术的发展,这些问题也出现了对应的解决方案。
第二个问题就是地址空间被隔离。
内存地址只有在同一台机器上才是有效的,在一台机器上可以通过共享内存来实现地址空间不被隔离,但在跨网络上地址空间是完全隔离的。比如在使用指针时,本地地址空间中的指针在另一台机器上是没有意义的。
RPC虽然可以通过一些编程范式来隐蔽本地调用和远程调用的本质区别,但必须让开发者知道二者之间的区别。因为如果开发者不知道这个区别,还是按照本地调用的方式使用指针,则会出现不符合预期的结果,这无疑增加了开发者的开发成本。
第三个问题是局部故障。
如果所有服务部署在一台机器上,那么机器故障会导致机器上所有模块和系统出现故障。但在分布式架构中,不同服务被部署在不同的机器上,服务节点变多。当服务出现故障时,很有可能仅仅是其中一个或者几个节点发生故障。而部分节点故障,在早期没有一个公共的组件可以充当发现或者通知该节点故障的角色。但是现在,可以用注册中心来解决该问题,注册中心的相关内容在《深入理解RPC框架原理与实现》一书的第3章会详细介绍。
除了故障的发现和通知需要引入新的组件,故障类型也因为RPC而变得模糊,在定位问题上变得复杂。比如局部故障问题中网络链路的故障与该链路上远程机器的处理器故障就无法区分。除了发现问题和定位问题的难度上升,在解决局部故障上的难度也不小。因为出现局部故障后,需要保证整个集群的处理结果是一致的。
比如需要通过分布式事务解决方案来保证整个集群所有节点写入数据的操作是一致的,不会因为局部故障而出现故障节点写入失败但是非故障节点写入数据成功,导致无法保证数据一致性的问题。这些故障相关的问题都是因为引入RPC后才出现的。
第四个问题就是并发问题。
在分布式架构中,每个服务都有多个节点,如果多个节点同时对某个服务发起调用,就会产生并发问题,并发问题会导致各种意想不到的结果。虽然本地调用也存在多线程的并发调用问题,但非分布式架构是完全可以控制调用顺序的,然而分布式架构引入了真正的异步操作调用,无法做到完全控制调用顺序。因为每个节点在不同的机器上,它们发起调用的时间没有被统一管控,也无法管控。
以上是RPC引入的问题,它们也是分布式架构面临的挑战。虽然这些问题的确挺棘手,但这些问题也慢慢有了对应的解决方案或者改善方案,让这些问题不再阻碍分布式架构的发展。
本文摘自《深入理解RPC框架原理与实现》一书,欢迎阅读此书了解更多有关RPC框架的内容!
▊《深入理解RPC框架原理与实现》 华钟明 著
本书的内容涵盖了这三部分,除了介绍市面上主流的RPC框架,还介绍了使用这些RPC框架的示例,方便读者通过这些示例上手RPC框架。除此之外,本书还介绍了对RPC框架的选型,为读者提供选型指南。
在介绍RPC框架的核心组成部分时,对每一个核心组成部分,本书都会完整地介绍该部分的周边知识,旨在让该领域的新手读者也能够轻松理解。除此之外,在介绍每一个核心组成部分时,本书都会介绍业界不同的实现方案,加深读者对这一核心组成部分的理解。本书还提供了一个实现简易的RPC框架的示例,通过动手实现RPC框架,可加深读者对RPC框架实现原理的认知,不单单停留在理论层面,而是能够直接运用RPC技术理论编写RPC框架。