接下来我们就讲RPC:远程过程调用。其实RPC仅仅是一个概念,RPC没有协议没有框架,平时我们说的RPC框架其实是简称,很多人被带偏了,以为RPC是一个协议或者是框架,所以你以后如果听到RPC框架,你要立马意识到,它的含义是:符合RPC概念的一个技术框架。实现的框架有rmi、dubbo、Hessian
那什么是RPC?一句话:像调用本地方法一样调用远程服务上的方法。
下面我们举例说明,有一个集群中的应用偶尔计算大数的阶乘,服务器根本受不了(下图1),如果升级服务器那就要升级几十台,经费不够,所以有人提出来,买一台性能超级优秀的服务器,将有压力的算法独立出一个应用放到这台服务器,接收http请求,计算完成后返回,这样问题就解决了(下图2)。
但是时间长了研发发现,每次有个新的高压力方法就需要在应用侧添加一个http请求代码块,服务端要加一个接收代码块,它们行为逻辑基本是一样的(下图红色),并且传递参数也非常麻烦(下图蓝色,最简单的例子就是:调用方法传递了一个pojo对象参数很简单,但是网络通信的时候,你可能就要将pojo中每个属性作为的参数发往服务端,然后服务端再逐个获取在封装成pojo对象),这直接造成了代码的冗余及调用的不便利。
能不能调用将这些冗余的统一起来,并将请求的方式整成在像在同个应用中调用,当然可以(如下图)
服务端抽象出一个接口,然后实现这个接口,然后注册到一个map中,k-v分别为:接口名 => 实现类的实例。
客户端将服务端的接口复制一份到应用中,然后程序启动时自动生成一个这个接口的代理类,后面直接调用这个代理类方法,而这个代理类通过网络通信,将接口名、方法名、序列化的参数数据发送到服务端。
服务端收到接口名,然后从map中找到实现类的实例,然后再将参数数据反序列化变为对象,接着根据获得的方法参数使用反射技术调用实例的方法。然后将执行结果序列化通过网络返回给客户端,客户端的代理类将数据反序列化为对象后再返回给调用方。对客户端程序开发者而言,就像是调用当前应用内的方法。
上面的两个概念:
1.序列化:在程序执行中一个对象中属性使用的空间并不是固定大小的(例如:字符串属性),并且也不是使用的连续内存(不同时间申请的空间,肯定做不到连续),所以我们做不到指定开始结束两个位置来复制内存数据直接传输,所以我们要开辟一块连续的内存,并将对象的所有数据按照指定的格式(譬如用竖线‘|’分隔)隔离数据的意义放到这段内存中。我们用一个生活的例子:你有多个储蓄罐,每个储蓄罐有一种用途,一个是买糖的,一个是买盐的,现在你钱存够了,然后要让别帮你买东西,你第一步要把每个储蓄罐中的钱都拿出来放到他手上(就是上面不连续的数据变成连续的),第二你要告诉他这些钱要分几份,每份多少钱,要买什么的(隔离数据的意义)。
2.stub:我们在rpc中经常会遇到这个词,意思是:存根、票据,那这个是什么意思呢?其实就是记录将来要使用的一些信息。举例就是:你记性不好,看到一家肯德基店,你就把位置在纸上记下,等到星期四了,你就去拿起那张纸看下位置在哪里,然后去买,这张纸就是存根。在rpc通信中stub就代表了服务端的相关信息。