15.1 Spring 远程调用概览

远程调用是客户端应用和服务端之间的会话。在客户端,它所需要的一些功能并不在该应用的实现范围之内,所以应用要向能提供这些功能的其他系统寻求帮助。而远程应用通过远程服务暴露这些功能。

假设我们想把 Spittr 应用中的某些功能发布为远程服务并提供给其他应用来使用。或许除了现有的基于浏览器的用户界面,我们还想为 Spittr 应用提供桌面应用或移动端应用,如图 15.1 所示。为了实现此想 法,我们需要把 SpitterService 接口的基本功能发布为远程服务。

其他应用与 Spittr 之间的会话开始于客户端应用的一个远程过程调用(remote procedure call,RPC)。从表面上看,RPC 类似于调用一个本地对象的一个方法。这两者都是同步操作,会阻塞调用代码的执行,直到被调用的过程执行完毕。

它们的差别仅仅是距离的问题,类似于人与人之间的交流。如果我们在公共场所的饮水机旁讨论周末足球比赛的结果,那我们就是在进行一个本地会话 —— 两人之间的会话发生在同一房间内。同样,本地方法调用是指同一个应用中的两个代码块之间的执行流交换。

另一方面,如果我们拿起电话打给另一个城市的客户端,那我们之间的会话就是通过电话网络远程进行的。类似地,RPC 调用就是执行流从一个应用传递给另一个应用,理论上另一个应用部署在跨网络的一台远程机器上。

正如我之前所述,Spring 支持多种不同的 RPC 模型,包括 RMI、 Caucho 的 Hessian 和 Burlap 以及 Spring 自带的 HTTP invoker。表 15.1 概述了每一个 RPC 模型,并简要讨论了它们所适用的不同场景。

RPC 模型

适用场景

远程方法调用 (RMI)

不考虑网络限制时(例如防火墙),访问/发布基于 Java 的服务

Hessian 或 Burlap

考虑网络限制时,通过 HTTP 访问/发布基于 Java 的服务。Hessian 是二进制协议,而 Burlap 是基于 XML 的

HTTP invoker

考虑网络限制,并希望使用基于 XML 或专有的序列化机制实现 Java 序列化时,访问/发布基于 Spring 的服务

JAX-RPC 和 JAX-WS

访问/发布平台独立的、基于 SOAP 的 Web 服务

不管你选择哪种远程调用模型,我们会发现 Spring 都提供了风格一致的支持。这意味着一旦理解了如何配置 Spring 来使用其中的一种模型,如果我们决定使用另外一种模型的话,将拥有非常低的学习曲线。

在所有的模型中,服务都作为 Spring 所管理的 bean 配置到我们的应用中。这是通过一个代理工厂 bean 实现的,这个 bean 能够把远程服务像本地对象一样装配到其他 bean 的属性中去。图 15.2 展示了它是如何工作的。

客户端向代理发起调用,就像代理提供了这些服务一样。代理代表客户端与远程服务进行通信,由它负责处理连接的细节并向远程服务发起调用。

更重要的是,如果调用远程服务时发生 java.rmi.RemoteException 异常,代理会处理此异常并重新抛出非检查型异常 RemoteAccessException。远程异常通常预示着系统发生了无法优雅恢复的问题,如网络或配置问题。既然客户端通常无法从远程异常中恢复,那么重新抛出 RemoteAccessException 异常就能让客户端来决定是否处理此异常。

在服务器端,我们可以使用表 15.1 所列出的任意一种模型将 Spring 管理的 bean 发布为远程服务。图 15.3 展示了远程导出器(remote exporter)如何将 bean 方法发布为远程服务。

无论我们开发的是使用远程服务的代码,还是实现这些服务的代码,或者两者兼而有之,在 Spring 中,使用远程服务纯粹是一个配置问题。我们不需要编写任何 Java 代码就可以支持远程调用。我们的服务 bean 也不需要关心它们是否参与了一个 RPC(当然,任何传递给远程调用的 bean 或从远程调用返回的 bean 可能需要实现 java.io.Serializable 接口)。

让我们通过 RMI —— Java 最初的远程调用技术 —— 来开始探索 Spring 对远程调用的支持吧。

Last updated