RPC
分布式基础
- 为什么要使用RPC
- 分布式的定义及解决问题的方式非常简单,即业务服务器接收到用户的计算请求后根据特定的算法,将计算的不同部分交由后台的不同主机处理,待结果汇总后,由业务服务器反馈给客户端。
- 在这个过程中,开发人员最为关注的就是计算的分发调度方式,因为这涉及到对网络通讯和并发的控制。
- 如果有一种方式能够将网络通讯和并发控制对程序开发人员透明化,那么将极度简化此类应用的开发成本,RMI就是这样一个范例。
- 如何实现网络通讯和并发控制的透明化?
- 将调用网络的业务的具体实现方法,就像调用本地的方法一样。
- 使用RPC完成
- RPC概念
- RPC(Remote Procedure Call Protocol):远程过程调用:
- 两台服务器A、B,分别部署不同的应用a,b。当A服务器想要调用B服务器上应用b提供的函数或方法的时候,由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义传达调用的数据。
- 即在远程端的机器上写了一个程序,客户端这边是无法直接调用的,这个时候就出现了一个远程服务调用的概念。
- RPC是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。RPC采用客户端/服务器模式。请求程序就是一个客户机,而服务提供程序就是一个服务器。首先,客户机调用进程发送一个有进程参数的调用信息到服务进程,然后等待应答信息。在服务器端,进程保持睡眠状态直到调用信息到达为止。当一个调用信息到达,服务器获得进程参数,计算结果,发送答复信息,然后等待下一个调用信息,最后,客户端调用进程接收答复信息,获得进程结果,然后调用执行继续进行。
- RPC需要解决的问题
- 通讯问题
- 寻址问题
- 序列化与反序列化
- 具体描述:
- 通讯问题 :
- 主要是通过在客户端和服务器之间建立TCP连接,远程过程调用的所有交换的数据都在这个连接里传输。连接可以是按需连接,调用结束后就断掉,也可以是长连接,多个远程过程调用共享同一个连接。
- 寻址问题
- A服务器上的应用怎么告诉底层的RPC框架,如何连接到B服务器(如主机或IP地址)以及特定的端口,方法的名称名称是什么,这样才能完成调用。比如基于Web服务协议栈的RPC,就要提供一个endpoint URI,或者是从UDDI服务上查找。如果是RMI调用的话,还需要一个RMI Registry来注册服务的地址。
- 序列化与反序列化 :
- 当A服务器上的应用发起远程过程调用时,方法的参数需要通过底层的网络协议如TCP传递到B服务器,由于网络协议是基于二进制的,内存中的参数的值要序列化成二进制的形式,也就是序列化或编组,通过寻址和传输将序列化的二进制发送给B服务器。同理,B服务器接收参数要将参数反序列化。B服务器应用调用自己的方法处理后返回的结果也要序列化给A服务器,A服务器接收也要经过反序列化的过程。
- 通讯问题 :
- Java中的RPC分布式规范:RMI
- 远程方法调用(Remote Method Invocation);
- 能够让在某个Java虚拟机上的对象调用本地对象一样调用另一个Java虚拟机的对象上的方法。
- RMI使用过程中需要的环境
- RMI是Java的一组拥护开发分布式应用程序的API。
- RMI可以被看作是RPC的Java版本。
- RMI使用的一般流程:
- 客户对象调用客户端辅助对象上的方法。
- 客户端辅助对象打包调用信息(变量,方法名),通过网络发送给服务端辅助对象;
- 服务端辅助对象将客户端辅助对象发送来的信息解包,找出真正被调用的方法以及该方法所在对象;
- 调用真正服务对象上的真正方法,并将结果返回给服务端辅助对象;
- 服务端辅助对象将结果打包,发送给客户端辅助对象;
- 客户端辅助对象将返回值解包,返回给客户对象;
- 客户端获取返回值。
- 做到透明化:步骤2~6是完全透明的,开发人员无需关注任何的网络数据传输处理。
- RMI使用范例
- 服务端
- 定义的业务接口要继承Remote
- 在业务接口的方法声明处要抛出RemoteException
- 在业务逻辑实现的类中继承UnicastRemoteObject
- 在业务逻辑的实现类的构造方法上也要抛出RemoteException
- 业务逻辑接收的参数和返回值:均需要可序列化(都需要通过网络传输)
- 指定对外服务的端口号LocateRegistry.createRegistry(9999)
- 使用Naming绑定URL,指定服务的识别名称Naming.bind("rmi://127.0.0.1:9999/Hello", biz);
- 客户端
- 接口和服务器端完全一致
- 使用Naming寻址,找到提供服务的远程业务方法Naming.lookup("rmi://127.0.0.1:9999/Hello");
- 返回与指定 name 关联的远程对象的引用(一个 stub)。
- 调用该接口对象的具体方法。
- 服务端
- RMI的限制
- 为了满足计算任务调度的开发规范性和统一的异常处理,RMI对开发的流程规范比较严格,因此需要在业务代码中侵入较多的RMI特定API。
- 客户端参数传递即服务器端计算完成后的返回值传递使用了通用的序列化方式,这对于一些需要在完成分布式调度时对数据进行自定义的安全加密流程的项目来说,无疑是一个不好的消息,另一方面,RMI采用了标准的Socket通讯,这对于系统建立在其他网络通讯协议上的业务系统来说也是一个很大的障碍。
- RMI对于用非Java语言开发的应用系统的支持不足。
- 根据RMI的实现原理自行完成一套RPC模块
- 原理分析:
- RMI应用程序通常包括两个独立的程序:服务器程序和客户端程序。典型的服务器应用程序将创建多个远程对象,使这些远程对象能够被引用,然后等待客户机调用这些远程对象的方法。而典型的客户端程序则从服务器中得到一个或多个远程对象的引用,然后调用远程对象的方法。RMI为服务器和客户端进行通信和信息传递提供了一种机制。
- 在与远程对象的通信过程中,RMI使用标准机制:stub和skeleton。远程对象的stub担当远程对象的客户本地代表或代理人角色。调用程序将调用本地stub的方法,而本地stub将负责执行对远程对象的方法调用。在RMI中,远程对象的stub与该远程对象所实现的远程接口集相同。调用stub的方法时将执行下列操作:
- 初始化与包含远程对象的远程虚拟机的连接;
- 对远程虚拟机的参数进行编组(写入并传输);
- 等待方法调用结果;
- 解编(读取)返回值或返回的异常;
- 将值返回给调用程序。为了向调用程序展示比较简单的调用机制,stub将参数的序列化和网络级通信等细节隐藏了起来。在远程虚拟机中,每个远程对象都可以有相应的skeleton(在JDK1.2以后的环境中便无需开发人员编写具体Stub和skeleton,而是自动生成)。
- 使用Java的特征:
- Java网络开发
- Java反射机制
- 重要的常用设计模式:Java动态代理
- 原理分析:
- 反射
- JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
- 对于任意一个对象,都能够调用它的任意方法和属性;
- 这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
- Java反射机制主要提供了以下功能
- 在运行时判断任意一个对象所属的类;
- 在运行时构造任意一个类的对象;
- 在运行时判断任意一个类所具有的成员变量和方法;
- 在运行时调用任意一个对象的方法;
- 生成动态代理。
- 代理模式
- 代理模式是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个真实对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。通过使用代理,通常有两个优点:
- 可以隐藏委托类的实现;
- 可以实现客户与委托类间的解耦,在不修改委托类代码的情况下能够做一些额外的处理。
- 代理模式是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个真实对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。通过使用代理,通常有两个优点:
- 动态代理
- 可以动态地创建并代理,动态地处理对所代理方法的调用。
- 在动态代理上所做的所有调用都会被重定向到单一的调用处理器上,它的工作是揭示调用的类型并确定相应的策略。
- 示例代码:
- 被代理的业务接口
- 创建动态代理的处理器
- 构建代理对象并执行
- 被代理的业务接口
- 根据JDK动态代理实现机理的提示,我们可以自行利用多态实现无接口的动态代理,其结构如下:
- Java中通常使用什么方式实现网络和并发透明的便捷分布式RPC调用?
- Java中通常使用动态代理模式在代理处理器中完成分布式RPC调用的网络连接、数据传输和并发控制处理,由于Java的反射机制和部分动态特性,使用动态代理后,这些操作可以完全对用户(开发人员)透明,用户可以方便的以本地方法调用的方式调用远程端声明实现的业务方法(计算过程在远程端实现)