精华内容
下载资源
问答
  • RPC远程过程调用
    2022-04-14 20:04:43

    RPC, remote procedure call, 远程过程调用。简单的理解是一个节点请求另一个节点提供的服务

    • 本地过程调用:如果需要将本地student对象的age+1,可以实现一个addAge()方法,将student对象传入,对年龄进行更新之后返回即可,本地方法调用的函数体通过函数指针来指定。
    • 远程过程调用:上述操作的过程中,addAge()这个方法在服务端,执行函数的函数体在远程机器上

    远程过程调用该如何告诉机器需要调用这个方法呢?

    1. 首先客户端需要告诉服务器,需要调用的函数,这里函数和进程ID存在一个映射,客户端远程调用时,需要查一下函数,找到对应的ID,然后执行函数的代码。
    2. 客户端需要把本地参数传给远程函数,本地调用的过程中,直接压栈即可,但是在远程调用过程中不再同一个内存里,无法直接传递函数的参数,因此需要客户端把参数转换成字节流,传给服务端,然后服务端将字节流转换成自身能读取的格式,是一个序列化和反序列化的过程。
    3. 数据准备好了之后,如何进行传输?网络传输层需要把调用的ID和序列化后的参数传给服务端,然后把计算好的结果序列化传给客户端,因此TCP层即可完成上述过程,gRPC中采用的是HTTP2协议。



     

    更多相关内容
  • java-rpc远程过程调用

    2019-07-03 16:34:06
    纯java写的rpc远程过程调用列子,不依赖除jdk以外的任何第三方jar,简单,方便理解,代码几百行代码,小白都能看的懂,往架构方向发展的同学可以学习一下
  • PHPRPC是一个轻型的、安全的、跨网际的、跨语言的、跨平台的、跨环境的、跨域的、支持复杂对象传输的、支持引用参数传递的、支持内容输出重定向的、支持分级错误处理的、支持会话的、面向服务的高性能远程过程调用...
  • PHPRPC是一个轻型的、安全的、跨网际的、跨语言的、跨平台的、跨环境的、跨域的、支持复杂对象传输的、支持引用参数传递的、支持内容输出重定向的、支持分级错误处理的、支持会话的、面向服务的高性能远程过程调用...
  • PHPRPC是一个轻型的、安全的、跨网际的、跨语言的、跨平台的、跨环境的、跨域的、支持复杂对象传输的、支持引用参数传递的、支持内容输出重定向的、支持分级错误处理的、支持会话的、面向服务的高性能远程过程调用...
  • 浅析RPC远程过程调用基本原理

    万次阅读 多人点赞 2017-06-24 11:42:17
    在校期间大家都写过不少程序,比如写个hello world服务类,然后本地调用下,如下所示。这些程序的特点是服务消费方和服务提供方是本地调用关系。 而一旦踏入公司尤其是大型互联网公司就会发现,公司的系统都由...
    在校期间大家都写过不少程序,比如写个hello world服务类,然后本地调用下,如下所示。这些程序的特点是服务消费方和服务提供方是本地调用关系。

    而一旦踏入公司尤其是大型互联网公司就会发现,公司的系统都由成千上万大大小小的服务组成,各服务部署在不同的机器上,由不同的团队负责。这时就会遇到两个问题:1)要搭建一个新服务,免不了需要依赖他人的服务,而现在他人的服务都在远端,怎么调用?2)其它团队要使用我们的服务,我们的服务该怎么发布以便他人调用?下文我们将对这两个问题展开探讨。


    1 如何调用他人的远程服务?


    由于各服务部署在不同机器,服务间的调用免不了网络通信过程,服务消费方每调用一个服务都要写一坨网络通信相关的代码,不仅复杂而且极易出错。

    如果有一种方式能让我们像调用本地服务一样调用远程服务,而让调用者对网络通信这些细节透明,那么将大大提高生产力,比如服务消费方在执行helloWorldService.sayHello(“test”)时,实质上调用的是远端的服务。这种方式其实就是RPC(Remote Procedure Call Protocol),在各大互联网公司中被广泛使用,如阿里巴巴的hsf、dubbo(开源)、Facebook的thrift(开源)、Google grpc(开源)、Twitter的finagle等。

    要让网络通信细节对使用者透明,我们自然需要对通信细节进行封装,我们先看下一个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的目标就是要2~8这些步骤都封装起来,让用户对这些细节透明。


    1.1 怎么做到透明化远程服务调用?

    怎么封装通信细节才能让用户像以本地调用方式调用远程服务呢?对java来说就是使用代理!java代理有两种方式:1) jdk 动态代理;2)字节码生成。尽管字节码生成方式实现的代理更为强大和高效,但代码不易维护,大部分公司实现RPC框架时还是选择动态代理方式。

    下面简单介绍下动态代理怎么实现我们的需求。我们需要实现RPCProxyClient代理类,代理类的invoke方法中封装了与远端服务通信的细节,消费方首先从RPCProxyClient获得服务提供方的接口,当执行helloWorldService.sayHello(“test”)方法时就会调用invoke方法。



    1.2 怎么对消息进行编码和解码?


    1.2.1 确定消息数据结构

    上节讲了invoke里需要封装通信细节,而通信的第一步就是要确定客户端和服务端相互通信的消息结构。客户端的请求消息结构一般需要包括以下内容:

    1)接口名称

    在我们的例子里接口名是“HelloWorldService”,如果不传,服务端就不知道调用哪个接口了;

    2)方法名

    一个接口内可能有很多方法,如果不传方法名服务端也就不知道调用哪个方法;

    3)参数类型&参数值

    参数类型有很多,比如有bool、int、long、double、string、map、list,甚至如struct(class);

    以及相应的参数值;

    4)超时时间

    5)requestID,标识唯一请求id,在下面一节会详细描述requestID的用处。

    同理服务端返回的消息结构一般包括以下内容。

    1)返回值

    2)状态code

    3)requestID


    1.2.2 序列化

    一旦确定了消息的数据结构后,下一步就是要考虑序列化与反序列化了。

    什么是序列化?序列化就是将数据结构或对象转换成二进制串的过程,也就是编码的过程。

    什么是反序列化?将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程。

    为什么需要序列化?转换为二进制串后才好进行网络传输嘛!为什么需要反序列化?将二进制转换为对象才好进行后续处理!

    现如今序列化的方案越来越多,每种序列化方案都有优点和缺点,它们在设计之初有自己独特的应用场景,那到底选择哪种呢?从RPC的角度上看,主要看三点:1)通用性,比如是否能支持Map等复杂的数据结构;2)性能,包括时间复杂度和空间复杂度,由于RPC框架将会被公司几乎所有服务使用,如果序列化上能节约一点时间,对整个公司的收益都将非常可观,同理如果序列化上能节约一点内存,网络带宽也能省下不少;3)可扩展性,对互联网公司而言,业务变化快,如果序列化协议具有良好的可扩展性,支持自动增加新的业务字段,删除老的字段,而不影响老的服务,这将大大提供系统的健壮性。

    目前国内各大互联网公司广泛使用hessian、protobuf、thrift、avro等成熟的序列化解决方案来搭建RPC框架,这些都是久经考验的解决方案。

    1.3 通信

    消息数据结构被序列化为二进制串后,下一步就要进行网络通信了。目前有两种IO通信模型:1)BIO;2)NIO。一般RPC框架需要支持这两种IO模型,原理可参考:《一个故事讲清楚 NIO》。

    如何实现RPC的IO通信框架?1)使用java nio方式自研,这种方式较为复杂,而且很有可能出现隐藏bug,见过一些互联网公司使用这种方式;2)基于mina,mina在早几年比较火热,不过这些年版本更新缓慢;3)基于netty,现在很多RPC框架都直接基于netty这一IO通信框架,比如阿里巴巴的HSF、dubbo,Twitter的finagle等。

    1.4 消息里为什么要带有requestID?

    如果使用netty的话,一般会用channel.writeAndFlush()方法来发送消息二进制串,这个方法调用后对于整个远程调用(从发出请求到接收到结果)来说是一个异步的,即对于当前线程来说,将请求发送出来后,线程就可以往后执行了,至于服务端的结果,是服务端处理完成后,再以消息的形式发送给客户端的。于是这里出现以下两个问题:

    1)怎么让当前线程“暂停”,等结果回来后,再向后执行?

    2)如果有多个线程同时进行远程方法调用,这时建立在client server之间的socket连接上会有很多双方发送的消息传递,前后顺序也可能是随机的,server处理完结果后,将结果消息发送给client,client收到很多消息,怎么知道哪个消息结果是原先哪个线程调用的?

    如下图所示,线程A和线程B同时向client socket发送请求requestA和requestB,socket先后将requestB和requestA发送至server,而server可能将responseA先返回,尽管requestA请求到达时间更晚。我们需要一种机制保证responseA丢给ThreadA,responseB丢给ThreadB。


    怎么解决呢?

    1)client线程每次通过socket调用一次远程接口前,生成一个唯一的ID,即requestID(requestID必需保证在一个Socket连接里面是唯一的),一般常常使用AtomicLong从0开始累计数字生成唯一ID;

    2)将处理结果的回调对象callback,存放到全局ConcurrentHashMap里面put(requestID, callback);

    3)当线程调用channel.writeAndFlush()发送消息后,紧接着执行callback的get()方法试图获取远程返回的结果。在get()内部,则使用synchronized获取回调对象callback的锁,再先检测是否已经获取到结果,如果没有,然后调用callback的wait()方法,释放callback上的锁,让当前线程处于等待状态。

    4)服务端接收到请求并处理后,将response结果(此结果中包含了前面的requestID)发送给客户端,客户端socket连接上专门监听消息的线程收到消息,分析结果,取到requestID,再从前面的ConcurrentHashMap里面get(requestID),从而找到callback对象,再用synchronized获取callback上的锁,将方法调用结果设置到callback对象里,再调用callback.notifyAll()唤醒前面处于等待状态的线程。



    2 如何发布自己的服务?

    如何让别人使用我们的服务呢?有同学说很简单嘛,告诉使用者服务的IP以及端口就可以了啊。确实是这样,这里问题的关键在于是自动告知还是人肉告知。

    人肉告知的方式:如果你发现你的服务一台机器不够,要再添加一台,这个时候就要告诉调用者我现在有两个ip了,你们要轮询调用来实现负载均衡;调用者咬咬牙改了,结果某天一台机器挂了,调用者发现服务有一半不可用,他又只能手动修改代码来删除挂掉那台机器的ip。现实生产环境当然不会使用人肉方式。

    有没有一种方法能实现自动告知,即机器的增添、剔除对调用方透明,调用者不再需要写死服务提供方地址?当然可以,现如今zookeeper被广泛用于实现服务自动注册与发现功能!

    简单来讲,zookeeper可以充当一个服务注册表(Service Registry),让多个服务提供者形成一个集群,让服务消费者通过服务注册表获取具体的服务访问地址(ip+端口)去访问具体的服务提供者。如下图所示:


    具体来说,zookeeper就是个分布式文件系统,每当一个服务提供者部署后都要将自己的服务注册到zookeeper的某一路径上: /{service}/{version}/{ip:port}, 比如我们的HelloWorldService部署到两台机器,那么zookeeper上就会创建两条目录:分别为/HelloWorldService/1.0.0/100.19.20.01:16888 /HelloWorldService/1.0.0/100.19.20.02:16888。

    zookeeper提供了“心跳检测”功能,它会定时向各个服务提供者发送一个请求(实际上建立的是一个 socket 长连接),如果长期没有响应,服务中心就认为该服务提供者已经“挂了”,并将其剔除,比如100.19.20.02这台机器如果宕机了,那么zookeeper上的路径就会只剩/HelloWorldService/1.0.0/100.19.20.01:16888。

    服务消费者会去监听相应路径(/HelloWorldService/1.0.0),一旦路径上的数据有任务变化(增加或减少),zookeeper都会通知服务消费方服务提供者地址列表已经发生改变,从而进行更新。

    更为重要的是zookeeper 与生俱来的容错容灾能力(比如leader选举),可以确保服务注册表的高可用性。


    3 小结

    RPC几乎是每一个从学校进入互联网公司的同学都要首先学习的框架,之前面试过一个在大型互联网公司工作过两年的同学,对RPC还是停留在使用层面,这是不应该的。本文也仅是对RPC的一个比较粗糙的描述,希望对大家有所帮助,错误之处也请指出修正。

    4 一些开源的RPC框架

    https://github.com/alibaba/dubbo

    http://thrift.apache.org/?cm_mc_uid=87762817217214314008006&cm_mc_sid_50200000=1444181090


    转载链接:http://blog.jobbole.com/92290/


    展开全文
  • RPC远程过程调用简介

    2019-08-22 19:57:37
    1. 什么是RPC 远程过程调用(英语:Remote...如果涉及的软件采用面向对象编程,那么远程过程调用亦可称作远程调用或远程方法调用。 2. 背景与用途 在单台计算机中,我们可以通过程序调用来传递控制和数据;或者说通...

    1. 什么是RPC

    远程过程调用(英语:Remote Procedure Call,缩写为 RPC,也叫远程程序调用)是一个计算机通信协议。该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程。如果涉及的软件采用面向对象编程,那么远程过程调用亦可称作远程调用或远程方法调用。

    在这里插入图片描述

    2. 背景与用途

    在单台计算机中,我们可以通过程序调用来传递控制和数据;或者说通过程序调用,我们可以将多个程序组成一个整体来实现某个功能。

    如果将这种调用机制推广到多台彼此间可以进行网络通讯的计算机,由多台计算机中的多个程序组成一个整体来实现某个功能,这也是可以的。调用的一方(发起远程过程调用,然后调用这方的环境挂起,参数通过网络传递给被调用方,被调用的一方执行程序,当程序执行完成后,产生的结果再通过网络回传给调用的一方,调用的一方恢复继续执行。这样一种原型思想,就是我们所说的RPC远程过程调用。
    在这里插入图片描述RPC这种思想最早可以追溯到1976年,RPC的发展到今天已经40年有余了。

    如今的计算机应用中,单机性能上很难承受住产品的压力,需要不断扩充多台机器来提升整体的性能。同时为了充分利用这些集群里的计算机,需要对其从架构上进行划分,以提供不同的服务,服务间相互调用完成整个产品的功能。RPC就能帮助我们解决这些服务间的信息传递和调用。

    3. 概念说明

    关于RPC的概念,我们可以从广义和狭义来分别进行理解。

    广义
    我们可以将所有通过网络来进行通讯调用的实现统称为RPC。

    按照这样来理解的话,那我们发现HTTP其实也算是一种RPC实现。

    在这里插入图片描述狭义
    区别于HTTP的实现方式,在传输的数据格式上和传输的控制上独立实现。比如在机器间通讯传输的数据不采用HTTP协议的方式(分为起始行、header、body三部份),而是使用自定义格式的二进制方式。

    我们更多时候谈到的RPC都是指代这种狭义上的理解。

    4. 优缺点

    相比于传统HTTP的实现而言:

    优点

    • 效率高
    • 发起RPC调用的一方,在编写代码时可忽略RPC的具体实现,如同编写本地函数调用一样

    缺点

    • 通用性不如HTTP好
      因为传输的数据不是HTTP协议格式,所以调用双方需要专门实现的通信库,对于不同的编程开发语言,都要有相关实现。而HTTP作为一个标准协议,大部分的语言都已有相关的实现,通用性更好。

    HTTP更多的面向用户与产品服务器的通讯。

    RPC更多的面向产品内部服务器间的通讯。 thrift

    RPC结构

    RPC的设计思想是力图使远程调用中的通讯细节对于使用者透明,调用双方无需关心网络通讯的具体实现。因而实现RPC要进行一定的封装。

    RPC原理上是按如下结构流程进行实现的。
    在这里插入图片描述
    流程:
    调用者(Caller, 也叫客户端、Client)以本地调用的方式发起调用;
    Client stub(客户端存根,可理解为辅助助手)收到调用后,负责将被调用的方法名、参数等打包编码成特定格式的能进行网络传输的消息体;
    Client stub将消息体通过网络发送给对端(服务端)
    Server stub(服务端存根,同样可理解为辅助助手)收到通过网络接收到消息后按照相应格式进行拆包解码,获取方法名和参数;
    Server stub根据方法名和参数进行本地调用;
    被调用者(Callee,也叫Server)本地调用执行后将结果返回给server stub;
    Server stub将返回值打包编码成消息,并通过网络发送给对端(客户端);
    Client stub收到消息后,进行拆包解码,返回给Client;
    Client得到本次RPC调用的最终结果。

    展开全文
  • RPC远程过程调用详解

    千次阅读 2018-08-16 15:39:43
    RPC是指远程过程调用,也就是说两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数/方法,由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据。...

    RPC概念解析

    首先要知道RPC是什么,以及RPC能做什么。RPC是指远程过程调用,也就是说两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数/方法,由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据。比如说,一个方法可能是这样定义的: 
    Employee getEmployeeByName(String fullName)那么:

    1. 首先,要解决通讯的问题,主要是通过在客户端和服务器之间建立TCP连接,远程过程调用的所有交换的数据都在这个连接里传输。连接可以是按需连接,调用结束后就断掉,也可以是长连接,多个远程过程调用共享同一个连接。

    2. 第二,要解决寻址的问题,也就是说,A服务器上的应用怎么告诉底层的RPC框架,如何连接到B服务器(如主机或IP地址)以及特定的端口,方法的名称名称是什么,这样才能完成调用。比如基于Web服务协议栈的RPC,就要提供一个endpoint 
      URI,或者是从UDDI服务上查找。如果是RMI调用的话,还需要一个RMI Registry来注册服务的地址。

    3. 第三,当A服务器上的应用发起远程过程调用时,方法的参数需要通过底层的网络协议如TCP传递到B服务器,由于网络协议是基于二进制的,内存中的参数的值要序列化成二进制的形式,也就是序列化(Serialize)或编组(marshal),通过寻址和传输将序列化的二进制发送给B服务器。

    4. 第四,B服务器收到请求后,需要对参数进行反序列化(序列化的逆操作),恢复为内存中的表达方式,然后找到对应的方法(寻址的一部分)进行本地调用,然后得到返回值。

    5. 第五,返回值还要发送回服务器A上的应用,也要经过序列化的方式发送,服务器A接到后,再反序列化,恢复为内存中的表达方式,交给A服务器上的应用

      这里写图片描述 
      为什么RPC呢?就是无法在一个进程内,甚至一个计算机内通过本地调用的方式完成的需求,比如比如不同的系统间的通讯,甚至不同的组织间的通讯。由于计算能力需要横向扩展,需要在多台机器组成的集群上部署应用,RPC的协议有很多,比如最早的CORBA,Java RMI,Web Service的RPC风格,Hessian,Thrift,甚至Rest API。关于Netty而Netty框架不局限于RPC,更多的是作为一种网络协议的实现框架,比如HTTP,由于RPC需要高效的网络通信,就可能选择以Netty作为基础。除了网络通信,RPC还需要有比较高效的序列化框架,以及一种寻址方式。如果是带会话(状态)的RPC调用,还需要有会话和状态保持的功能。大体上来说,Netty就是提供一种事件驱动的,责任链式(也可以说是流水线)的网络协议实现方式。网络协议包含很多层次,很多部分组成,如传输层协议,编码解码,压缩解压,身份认证,加密解密,请求的处理逻辑,怎么能够更好的复用,扩展,业界通用的方法就是责任链,一个请求应答网络交互通常包含两条链,一条链(Upstream)是从传输层,经过一系列步骤,如身份认证,解密,日志,流控,最后到达业务层,一条链(DownStream)是业务层返回后,又经过一系列步骤,如加密等,又回到传输层。

    RPC 功能目标

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

    RPC 调用分类

    RPC 调用分以下两种:

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

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

    RPC 结构拆解

    如下图所示。

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

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

     

    RPC 组件职责

    上面我们进一步拆解了 RPC 实现结构的各个组件组成部分,下面我们详细说明下每个组件的职责划分。

    1. RpcServer
      负责导出(export)远程接口
    2. RpcClient
      负责导入(import)远程接口的代理实现
    3. RpcProxy
      远程接口的代理实现
    4. RpcInvoker
      客户方实现:负责编码调用信息和发送调用请求到服务方并等待调用结果返回
      服务方实现:负责调用服务端接口的具体实现并返回调用结果
    5. RpcProtocol
      负责协议编/解码
    6. RpcConnector
      负责维持客户方和服务方的连接通道和发送数据到服务方
    7. RpcAcceptor
      负责接收客户方请求并返回请求结果
    8. RpcProcessor
      负责在服务方控制调用过程,包括管理调用线程池、超时时间等
    9. RpcChannel
      数据传输通道

    RPC 实现分析

    在进一步拆解了组件并划分了职责之后,这里以在 java 平台实现该 RPC 框架概念模型为例,详细分析下实现中需要考虑的因素。

    导出远程接口

    导出远程接口的意思是指只有导出的接口可以供远程调用,而未导出的接口则不能。 在 java 中导出接口的代码片段可能如下:

    DemoService demo   = new ...;

    RpcServer   server = new ...;

    server.export(DemoService.class, demo, options);

    我们可以导出整个接口,也可以更细粒度一点只导出接口中的某些方法,如:

    // 只导出 DemoService 中签名为 hi(String s) 的方法

    server.export(DemoService.class, demo, "hi", new Class<?>[] { String.class }, options);

    java 中还有一种比较特殊的调用就是多态,也就是一个接口可能有多个实现,那么远程调用时到底调用哪个? 这个本地调用的语义是通过 jvm 提供的引用多态性隐式实现的,那么对于 RPC 来说跨进程的调用就没法隐式实现了。 如果前面 DemoService 接口有 2 个实现,那么在导出接口时就需要特殊标记不同的实现,如:

    1

    2

    3

    4

    5

    DemoService demo   = new ...;

    DemoService demo2  = new ...;

    RpcServer   server = new ...;

    server.export(DemoService.class, demo, options);

    server.export("demo2", DemoService.class, demo2, options);

    上面 demo2 是另一个实现,我们标记为 demo2 来导出, 那么远程调用时也需要传递该标记才能调用到正确的实现类,这样就解决了多态调用的语义。

    导入远程接口与客户端代理

    导入相对于导出远程接口,客户端代码为了能够发起调用必须要获得远程接口的方法或过程定义。 目前,大部分跨语言平台 RPC 框架采用根据 IDL 定义通过 code generator 去生成 stub 代码, 这种方式下实际导入的过程就是通过代码生成器在编译期完成的。 我所使用过的一些跨语言平台 RPC 框架如 CORBAR、WebService、ICE、Thrift 均是此类方式。

    代码生成的方式对跨语言平台 RPC 框架而言是必然的选择,而对于同一语言平台的 RPC 则可以通过共享接口定义来实现。 在 java 中导入接口的代码片段可能如下:

    RpcClient client = new ...;

    DemoService demo = client.refer(DemoService.class);

    demo.hi("how are you?");

    在 java 中 import 是关键字,所以代码片段中我们用 refer 来表达导入接口的意思。 这里的导入方式本质也是一种代码生成技术,只不过是在运行时生成,比静态编译期的代码生成看起来更简洁些。 java 里至少提供了两种技术来提供动态代码生成,一种是 jdk 动态代理,另外一种是字节码生成。 动态代理相比字节码生成使用起来更方便,但动态代理方式在性能上是要逊色于直接的字节码生成的,而字节码生成在代码可读性上要差很多。 两者权衡起来,个人认为牺牲一些性能来获得代码可读性和可维护性显得更重要。

    协议编解码

    客户端代理在发起调用前需要对调用信息进行编码,这就要考虑需要编码些什么信息并以什么格式传输到服务端才能让服务端完成调用。 出于效率考虑,编码的信息越少越好(传输数据少),编码的规则越简单越好(执行效率高)。 我们先看下需要编码些什么信息:

    调用编码

    1. 接口方法
      包括接口名、方法名
    2. 方法参数
      包括参数类型、参数值
    3. 调用属性
      包括调用属性信息,例如调用附件隐式参数、调用超时时间等

    返回编码

    1. 返回结果
      接口方法中定义的返回值
    2. 返回码
      异常返回码
    3. 返回异常信息
      调用异常信息

    除了以上这些必须的调用信息,我们可能还需要一些元信息以方便程序编解码以及未来可能的扩展。 这样我们的编码消息里面就分成了两部分,一部分是元信息、另一部分是调用的必要信息。 如果设计一种 RPC 协议消息的话,元信息我们把它放在协议消息头中,而必要信息放在协议消息体中。 下面给出一种概念上的 RPC 协议消息设计格式:

    消息头

     

    • magic : 协议魔数,为解码设计
    • header size: 协议头长度,为扩展设计
    • version : 协议版本,为兼容设计
    • st : 消息体序列化类型
    • hb : 心跳消息标记,为长连接传输层心跳设计
    • ow : 单向消息标记,
    • rp : 响应消息标记,不置位默认是请求消息
    • status code: 响应消息状态码
    • reserved : 为字节对齐保留
    • message id : 消息 id
    • body size : 消息体长度

    消息体

    采用序列化编码,常见有以下格式

    • xml : 如 webservie SOAP
    • json : 如 JSON-RPC
    • binary: 如 thrift; hession; kryo 等

    格式确定后编解码就简单了,由于头长度一定所以我们比较关心的就是消息体的序列化方式。 序列化我们关心三个方面:

    1. 序列化和反序列化的效率,越快越好。
    2. 序列化后的字节长度,越小越好。
    3. 序列化和反序列化的兼容性,接口参数对象若增加了字段,是否兼容。

    上面这三点有时是鱼与熊掌不可兼得,这里面涉及到具体的序列化库实现细节,就不在本文进一步展开分析了。

    传输服务

    协议编码之后,自然就是需要将编码后的 RPC 请求消息传输到服务方,服务方执行后返回结果消息或确认消息给客户方。 RPC 的应用场景实质是一种可靠的请求应答消息流,和 HTTP 类似。 因此选择长连接方式的 TCP 协议会更高效,与 HTTP 不同的是在协议层面我们定义了每个消息的唯一 id,因此可以更容易的复用连接。

    既然使用长连接,那么第一个问题是到底 client 和 server 之间需要多少根连接? 实际上单连接和多连接在使用上没有区别,对于数据传输量较小的应用类型,单连接基本足够。 单连接和多连接最大的区别在于,每根连接都有自己私有的发送和接收缓冲区, 因此大数据量传输时分散在不同的连接缓冲区会得到更好的吞吐效率。 所以,如果你的数据传输量不足以让单连接的缓冲区一直处于饱和状态的话,那么使用多连接并不会产生任何明显的提升, 反而会增加连接管理的开销。

    连接是由 client 端发起建立并维持。 如果 client 和 server 之间是直连的,那么连接一般不会中断(当然物理链路故障除外)。 如果 client 和 server 连接经过一些负载中转设备,有可能连接一段时间不活跃时会被这些中间设备中断。 为了保持连接有必要定时为每个连接发送心跳数据以维持连接不中断。 心跳消息是 RPC 框架库使用的内部消息,在前文协议头结构中也有一个专门的心跳位, 就是用来标记心跳消息的,它对业务应用透明。

    执行调用

    client stub 所做的事情仅仅是编码消息并传输给服务方,而真正调用过程发生在服务方。 server stub 从前文的结构拆解中我们细分了 RpcProcessor 和 RpcInvoker 两个组件, 一个负责控制调用过程,一个负责真正调用。 这里我们还是以 java 中实现这两个组件为例来分析下它们到底需要做什么?

    java 中实现代码的动态接口调用目前一般通过反射调用。 除了原生的 jdk 自带的反射,一些第三方库也提供了性能更优的反射调用, 因此 RpcInvoker 就是封装了反射调用的实现细节。

    调用过程的控制需要考虑哪些因素,RpcProcessor 需要提供什么样地调用控制服务呢? 下面提出几点以启发思考:

    1. 效率提升
      每个请求应该尽快被执行,因此我们不能每请求来再创建线程去执行,需要提供线程池服务。
    2. 资源隔离
      当我们导出多个远程接口时,如何避免单一接口调用占据所有线程资源,而引发其他接口执行阻塞。
    3. 超时控制
      当某个接口执行缓慢,而 client 端已经超时放弃等待后,server 端的线程继续执行此时显得毫无意义。

    RPC 异常处理

    无论 RPC 怎样努力把远程调用伪装的像本地调用,但它们依然有很大的不同点,而且有一些异常情况是在本地调用时绝对不会碰到的。 在说异常处理之前,我们先比较下本地调用和 RPC 调用的一些差异:

    1. 本地调用一定会执行,而远程调用则不一定,调用消息可能因为网络原因并未发送到服务方。
    2. 本地调用只会抛出接口声明的异常,而远程调用还会跑出 RPC 框架运行时的其他异常。
    3. 本地调用和远程调用的性能可能差距很大,这取决于 RPC 固有消耗所占的比重。

    正是这些区别决定了使用 RPC 时需要更多考量。 当调用远程接口抛出异常时,异常可能是一个业务异常, 也可能是 RPC 框架抛出的运行时异常(如:网络中断等)。 业务异常表明服务方已经执行了调用,可能因为某些原因导致未能正常执行, 而 RPC 运行时异常则有可能服务方根本没有执行,对调用方而言的异常处理策略自然需要区分。

    由于 RPC 固有的消耗相对本地调用高出几个数量级,本地调用的固有消耗是纳秒级,而 RPC 的固有消耗是在毫秒级。 那么对于过于轻量的计算任务就并不合适导出远程接口由独立的进程提供服务, 只有花在计算任务上时间远远高于 RPC 的固有消耗才值得导出为远程接口提供服务。

    总结

    至此我们提出了一个 RPC 实现的概念框架,并详细分析了需要考虑的一些实现细节。 无论 RPC 的概念是如何优雅,但是“草丛中依然有几条蛇隐藏着”,只有深刻理解了 RPC 的本质,才能更好地应用。

    展开全文
  • PHPRPC是一个轻型的、安全的、跨网际的、跨语言的、跨平台的、跨环境的、跨域的、支持复杂对象传输的、支持引用参数传递的、支持内容输出重定向的、支持分级错误处理的、支持会话的、面向服务的高性能远程过程调用...
  • firda rpc 远程调用 xposed rpc 远程调用 这些详细的课程很多培训课程里面都有今天我也来个实战把 开始正文 视频演示:: https://www.bilibili.com/video/BV1BL4y147it/ 主要是实战下。不知道怎么表达出来,看实战把...
  • 文章目录Dubbo的RPC远程过程调用+Dubbo的负载均衡+Zookeeper注册中心...测试总结dubbo的RPC远程过程调用监控中心——Simple Monitor安装配置Dubbo与Spring Boot整合提供者服务整合Spring Boot消费者服务整合Spring
  • RPC(Remote Procedure Call,远程过程调用)是一种计算机程序通信方式,允许运行于一台计算机中的程序调用运行于另一台计算机种的子程序。如果涉及的程序采用了面向对象编程,那么 RPC 亦可称作 “远程函数/方法...
  • RPC远程过程调用协议)

    千次阅读 2022-04-15 16:01:47
    RPC的简单介绍
  • RPC远程过程调用:后来,我用了RPC(远程过程调用)的方式来发请求,远程过程调用的功能就是将远程的方法当作本地的方法一样调用,其内部的实现机制比如有基于Http协议的httpClient 、Java的Socket,...
  • 一、RMI 远程方法调用 RMI(Remote Method Invocation)远程方法调用。能够让在客户端Java虚拟机上的对象像调用本地对象一样调用服务端java 虚拟机中的对象上的方法。使用代表:EJB RMI远方法程调用步骤: 1、...
  • Windows RPC 远程过程调用

    千次阅读 2016-08-17 17:05:31
    本文章转载自 ... ...一、什么是远程过程调用 ... 什么是远程过程调用 RPC(Remote Procedure Call)? 你可能对这个概念有点陌生, 而你可能非常熟悉 NFS, 是的,  NFS 就是基于 RPC 的. 为了
  • RPC远程过程调用)详解

    万次阅读 多人点赞 2019-03-16 11:59:47
      RPC是指远程过程调用,也就是说两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数/方法,由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据。...
  • 此文档对于RPC远程过程调用做了较为详细的介绍,对于初学者来说了解RPC协议应该有所帮助,希望读者满意。
  • windows远程过程调用,详细讲解如何编写RPC程序
  • php rpc远程过程调用

    2009-01-07 21:32:33
    很简单的远程过程调用支持包, 比web service更简单的体现远程调用
  • RPC简单介绍
  • RPC 远程过程调用协议

    2017-02-14 14:05:04
    RPC(Remote Procedure Call Protocol)—— 远程过程调用协议 RPC是指远程过程调用,也就是说两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数/方法,由于不在一个内存空间,不能直接...
  • 他允许程序调用另一个地址空间的过程或函数,而不用程序员显示编码这个远程调用的细节。即程序员无论是调用本地的还是远程的函数,本质上编写的调用代码基本相同。 有两台服务器A、B,一个应用部署在A服务器上,想要...
  • 主要介绍了通过 Redis 实现 RPC 远程方法调用,支持多种编程语言,本文就以Ruby和Python为例,给出了实现代码,需要的朋友可以参考下
  • 可以单独将这个框架放在一个服务器,通过ip给服务提供者和服务消费者远程连接 2.单独开两个idea,分别按顺序执行服务提供者、服务消费者 左边是服务提供者:可以看到在配置文件中含有protocol; 右边是服务消费...
  • ThinkPHP框架如何开发RPC接口 RPC微服务与框架结合,原框架源码已经修改 phprpc解决方案
  • Windows RPC远程过程调用(入门)

    万次阅读 2018-07-12 15:19:12
    前言: 编写一个win32 RPC demo具体内容: 1:编写IDL(Interface Description Language,接口描述语言): test.idl:[ uuid("F211474B-E9DF-4682-B779-DFAA64B56F9F"), version(1.0) ] interface ...
  •  服务器开发中会使用RPC(Remote Procedure Call,远程过程调用)简化进程间通信的过程RPC能有效地封装通信过程RPC能有效地封装通信过程,让远程的数据收发通信过程看起来就像本地的函数调用一样。  本例中,...
  • Java中的远程过程调用(RPC)

    千次阅读 2020-05-06 16:50:47
    这学期上了《分布式系统》课程,内容主要是基于Java实现分布式计算,所以老师前几节课主要在给我们讲用Java做分布式...为了方便学习和记录,我将老师讲的内容结合书籍和资料做了一些整理,这一篇主要讨论远程过程调用

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 117,237
精华内容 46,894
关键字:

RPC远程过程调用