浅谈-RPC

47次阅读

共计 3005 个字符,预计需要花费 8 分钟才能阅读完成。

本文来自于 Dubbo 官网:浅谈 RPC,转载请保留链接 ;)

近几年随着微服务化项目的崛起,逐渐成为许多公司中大型分布式系统架构的主流方式,而今天所说的 RPC 在这其中扮演着至关重要的角色。随着这段日子公司项目微服务化的演进,发现在日常开发中都在隐式或显式的使用 RPC,一些刚刚接触 RPC 的小伙伴会感觉无所适从,而一些入行多年的老手虽然使用 RPC 经验丰富,但有些对其原理也一知半解,缺乏对原理的深入理解,往往也会造成开发中的一些误用。

什么是 RPC?

RPC(Remote Procedure Call)—远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。也就是说两台服务器 A,B,一个应用部署在 A 服务器上,想要调用 B 服务器上应用提供的方法,由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据。

RPC 协议假定某些传输协议的存在,如 TCP 或 UDP,为通信程序之间携带信息数据。在 OSI 网络通信模型中,RPC 跨越了传输层和应用层。RPC 使得开发包括网络分布式多程序在内的应用程序更加容易。现在业界有很多开源的优秀 RPC 框架,例如 Spring Cloud、Dubbo、Thrift 等。

RPC 起源

RPC 这个概念术语在上世纪 80 年代由 Bruce Jay Nelson 提出。这里我们追溯下当初开发 RPC 的原动机是什么?在 Nelson 的论文 “Implementing Remote Procedure Calls” 中他提到了几点:

  • 简单:RPC 概念的语义十分清晰和简单,这样建立分布式计算就更容易。
  • 高效:过程调用看起来十分简单而且高效。
  • 通用:在单机计算中过程往往是不同算法部分间最重要的通信机制。

通俗一点说,就是一般程序员对于本地的过程调用很熟悉,那么我们把 RPC 作成和本地调用完全类似,那么就更容易被接受,使用起来毫无障碍。Nelson 的论文发表于 30 年前,其观点今天看来确实高瞻远瞩,今天我们使用的 RPC 框架基本就是按这个目标来实现的。

RPC 结构

Nelson 的论文中指出实现 RPC 的程序包括 5 个部分:

  1. User
  2. User-stub
  3. RPCRuntime
  4. Server-stub
  5. Server

这里 user 就是 client 端,当 user 想发起一个远程调用时,它实际是通过本地调用 user-stub。user-stub 负责将调用的接口、方法和参数通过约定的协议规范进行编码并通过本地的 RPCRuntime 实例传输到远端的实例。远端 RPCRuntime 实例收到请求后交给 server-stub 进行解码后发起本地端调用,调用结果再返回给 user 端。

以上是粗粒度的 RPC 实现概念结构,接下来我们进一步细化它应该由哪些组件构成,如下图所示。

RPC 服务方通过 RpcServer 去导出(export)远程接口方法,而客户方通过 RpcClient 去引入(import)远程接口方法。客户方像调用本地方法一样去调用远程接口方法,RPC 框架提供接口的代理实现,实际的调用将委托给代理 RpcProxy。代理封装调用信息并将调用转交给 RpcInvoker 去实际执行。在客户端的 RpcInvoker 通过连接器 RpcConnector 去维持与服务端的通道 RpcChannel,并使用 RpcProtocol 执行协议编码(encode)并将编码后的请求消息通过通道发送给服务方。

RPC 服务端接收器 RpcAcceptor 接收客户端的调用请求,同样使用 RpcProtocol 执行协议解码(decode)。解码后的调用信息传递给 RpcProcessor 去控制处理调用过程,最后再委托调用给 RpcInvoker 去实际执行并返回调用结果。如下是各个部分的详细职责:

1. RpcServer  

   负责导出(export)远程接口  

2. RpcClient  

   负责导入(import)远程接口的代理实现  

3. RpcProxy  

   远程接口的代理实现  

4. RpcInvoker  

   客户方实现:负责编码调用信息和发送调用请求到服务方并等待调用结果返回  

   服务方实现:负责调用服务端接口的具体实现并返回调用结果  

5. RpcProtocol  

   负责协议编 / 解码  

6. RpcConnector  

   负责维持客户方和服务方的连接通道和发送数据到服务方  

7. RpcAcceptor  

   负责接收客户方请求并返回请求结果  

8. RpcProcessor  

   负责在服务方控制调用过程,包括管理调用线程池、超时时间等  

9. RpcChannel  

   数据传输通道 

RPC 工作原理

RPC 的设计由 Client,Client stub,Network,Server stub,Server 构成。其中 Client 就是用来调用服务的,Cient stub 是用来把调用的方法和参数序列化的(因为要在网络中传输,必须要把对象转变成字节),Network 用来传输这些信息到 Server stub,Server stub 用来把这些信息反序列化的,Server 就是服务的提供者,最终调用的就是 Server 提供的方法。

  1. Client 像调用本地服务似的调用远程服务;
  2. Client stub 接收到调用后,将方法、参数序列化
  3. 客户端通过 sockets 将消息发送到服务端
  4. Server stub 收到消息后进行解码(将消息对象反序列化)
  5. Server stub 根据解码结果调用本地的服务
  6. 本地服务执行 (对于服务端来说是本地执行) 并将结果返回给 Server stub
  7. Server stub 将返回结果打包成消息(将结果消息对象序列化)
  8. 服务端通过 sockets 将消息发送到客户端
  9. Client stub 接收到结果消息,并进行解码(将结果消息发序列化)
  10. 客户端得到最终结果。

RPC 调用分以下两种:

  1. 同步调用:客户方等待调用执行完成并返回结果。
  2. 异步调用:客户方调用后不用等待执行结果返回,但依然可以通过回调通知等方式获取返回结果。若客户方不关心调用返回结果,则变成单向异步调用,单向调用不用返回结果。

异步和同步的区分在于是否等待服务端执行完成并返回结果。

RPC 能干什么?

RPC 的主要功能目标是让构建分布式计算(应用)更容易,在提供强大的远程调用能力时不损失本地调用的语义简洁性。为实现该目标,RPC 框架需提供一种透明调用机制,让使用者不必显式的区分本地调用和远程调用,在之前给出的一种实现结构,基于 stub 的结构来实现。下面我们将具体细化 stub 结构的实现。

  • 可以做到分布式,现代化的微服务
  • 部署灵活
  • 解耦服务
  • 扩展性强

RPC 的目的是让你在本地调用远程的方法,而对你来说这个调用是透明的,你并不知道这个调用的方法是部署哪里。通过 RPC 能解耦服务,这才是使用 RPC 的真正目的。

总结

这篇文章介绍了 RPC 的一些基本原理,相信到这里您已经对 RPC 有了一定理解。其实发现实现一个 RPC 不算难,难的是实现一个高性能高可靠的 RPC 框架。比如,既然是分布式了,那么一个服务可能有多个实例,你在调用时,要如何获取这些实例的地址呢?这时候就需要一个服务注册中心,比如在 Dubbo 中,就可以使用 Zookeeper 作为注册中心,在调用时,从 Zookeeper 获取服务的实例列表,再从中选择一个进行调用。那么选哪个调用好呢?这时候就需要负载均衡了,于是你又得考虑如何实现复杂均衡,比如 Dubbo 就提供了好几种负载均衡策略。所以请继续关注我的另外两篇文章 RPC 与服务化的关系注册中心,配置中心,服务发现浅谈,相信会帮助对 RPC 设计和实现有更多的理解。

正文完
 0

浅谈RPC

47次阅读

共计 1680 个字符,预计需要花费 5 分钟才能阅读完成。

从我们学习编程开始,就对『LPC』(local Procedure Call)十分熟悉,而 PRC 就是类似 LPC 的一种调用机制。在服务化、微服务化逐渐成为大中型分布式系统架构的主流方式的过程中,RPC 作为基本通用服务成为系统标配的一部分。
RPC 的全称是 Remote Procedure Call 是一种进程间通信方式。它允许程序调用另一个地址空间(通常是共享网络的另一台机器上)的过程或函数,而不用程序员显式编码这个远程调用的细节。即程序员无论是调用本地的还是远程的,本质上编写的调用代码基本相同。RPC 的概念源于 Bruce Jay Nelson 的论文 Implementing Remote Procedure Calls,有兴趣的可以阅读一下。
RPC 有许多成熟、开源的实现方案,这些方案均由大厂负责设计、开发,各有优点。grpc(google)https://github.com/grpc/grpcthrift(facebook):独特的序列化格式和 IDL,支持很多编程语言。thrift 的代码看似分层很清楚,client、server 选择很多,但没有一个足够通用,每个 server 实现都只能解决很小一块场景,每个 client 都线程不安全。实际使用中非常麻烦。thrift 的代码质量也比较差,接口和生成的代码都比较乱。https://github.com/apache/thriftdubbo(alibaba)https://github.com/alibaba/dubbosofa-pbrpc(baidu):百度 PS 基于 boost::asio 和 protobuf 实现的 RPC 框架,这个库代码工整,接口清晰,支持同步和异步。sofa-pbrpc 存在产品线自研框架的鲜明特点:不支持内部其他协议,对名字服务、负载均衡、服务认证、连接方式等多样化的需求的抽象不够一般化。https://github.com/baidu/sofa…baidu-rpc(baidu)提供稳定的 RPC 框架;适用各类业务场景,提供优秀的延时,吞吐,并发度,具备优秀的多核扩展性;接口易懂,用户体验佳。有完备的调试和运维接口(HTTP)。https://github.com/brpc/brpc。
RPC 原理和流程
1)服务消费方(client)调用以本地调用方式调用服务;2)client stub 接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体;3)client stub 找到服务地址,并将消息发送到服务端;4)server stub 收到消息后进行解码;5)server stub 根据解码结果调用本地的服务;6)本地服务执行并将结果返回给 server stub;7)server stub 将返回结果打包成消息并发送至消费方;8)client stub 接收到消息,并进行解码;9)服务消费方得到最终结果。
RPC 中需求关注的几个核心问题 1)序列化接口定义方面需要关注客户端和服务端各需要哪些数据来确保调用的正常返回、调用次序和调用结果正确表示。调用方:接口名称、方法名、参数类型及参数值、超时时间、requestID,标识唯一请求 id。返回方:返回值、状态码、requestID 传输协议方面现在最通用的方式是使用 protobuf,能够有效对数据进行压缩并提供高效的转换效率。另外常用的协议还包括 idl,百度内部使用 nshead+mcpack 等,facebook 自定义了格式化方法。2)通信模型:主流 RPC 框架都提供 BIO+NIO,提供同步、异步通信机制 3)使用 zookeeper、bns 服务提供服务发现。4)提供接口能够让用户自定义负载均衡算法。5)提供策略框架提供业务策略处理能力。
参考资料:1、http://birrell.org/andrew/pap…2、http://blog.jobbole.com/92290/3、http://blog.csdn.net/mindfloa…、http://blog.csdn.net/mindfloating/article/details/39474123

正文完
 0