原文: https://howtodoinjava.com/spring-boot2/spring-remoting-rmi-hessian/

在此 Spring boot 2 rmi 示例中,了解 spring 如何提供不同的机制来调用驻留在不同 JVM 中并且很可能在不同服务器中的远程方法。 这称为 RMI(远程方法调用)概念。

RMI 出现在 EJB 的早期。 在当今的 HTTP / HTTPS 世界中,SOAP 和 REST 在实现任何服务中都占主导地位,但是在 Spring 框架中,远程调用仍然是一种选择。 今天,我们将在这个领域看到两个新协议,主要是 HessianRMI

1. 什么是远程调用?

Spring 框架提供了一系列工具,这些工具统称为 Spring 远程调用。 这些工具使我们可以在远程系统上调用远程服务,就像它们在本地可用一样。

通常不需要,但是在某些情况下,我们仍然倾向于使用远程调用。 使用远程调用的主要原因是性能和安全性。 基于 HTTP 的服务会影响性能,因为我们必须处理请求和响应(封送/拆组)。 更重要的是,如果仅使用这些技术就已经将服务器公开为远程服务,则客户端除了 RMI 别无选择。

2. Spring 远程调用技术

以下主要是 spring 帮助我们实现远程调用的方法。

  • 远程方法调用(RMI) – 在 Spring,通过在服务器端使用RmiServiceExporter和在客户端使用RmiProxyFactoryBean实现了 RMI。

  • Spring 的 HTTP 调用程序 – 这是通过使用HttpInvokerProxyFactoryBeanHttpInvokerServiceExporter进行远程调用的另一种方式。 这基本上是通过 HTTP 的 Java 序列化,支持任何 Java 接口

  • Hessian – 它是 Caucho 提供的基于 HTTP 的轻量级二进制协议。 我们可以使用 Hessian Spring 的HessianProxyFactoryBeanHessianServiceExporter的 Spring 包装器来公开和使用远程服务。 内部在传输时使用二进制数据

  • Burlap – Burlap 是用于远程处理的基于 XML 的协议。 它也是由 Caucho 开发的。 它类似于 Hessian,唯一的区别是 Hessian 是二进制,而 Burlap 是 XML。 与 Hessian 一样,Burlap 需要通过 HTTP 托管。与 Hessian 相似,它具有BurlapServiceExporterBurlapProxyFactoryBean类。
    请注意,由于尚未积极开发 Burlap,自 Spring 4.0 起就不再支持它。

3. Spring RMI 远程调用 – 创建 RMI 服务

如上所述,我们将使用 Spring Boot 为 RMI 和 Hessian 开发项目。 我们将创建两个 Maven 项目。 服务器的第一个项目是托管实现,另一个是客户端部分。 客户端应用程序将通过相应的远程协议与服务器通信,以调用远程方法。

3.1. 创建 Maven 项目

创建两个名为spring-rmi-serverspring-rmi-client的简单的 Spring Boot 项目,它们具有spring-boot-web依赖,以将其托管在 Web 服务器中。

为此,我们需要转到 https://start.spring.io/ 并提供 Maven 坐标并选择依赖项。 下载包含框架项目的 zip 文件。 然后,一旦解压缩到合适的文件夹中,我们就需要将其导入 eclipse 中。

客户端项目中不需要 Web 依赖项,因为我们将其作为独立的应用程序运行。

Spring Boot Remoting – Spring RMI 注解示例 - 图1

Spring 项目创建

3.2. 创建 Spring RMI 服务

编写我们要公开的 Java 接口和 RMI 服务的实现。 在本教程中,我们将创建一个简单的 hello world 应用程序,该应用程序将根据输入向用户打招呼。 这是必需的接口及其实现。

HelloWorldRMI.java

  1. package com.example.springrmiserver.service;
  2. public interface HelloWorldRMI
  3. {
  4. public String sayHelloRmi(String msg);
  5. }

HelloWorldRMIimpl.java

  1. package com.example.springrmiserver.service;
  2. import java.util.Date;
  3. public class HelloWorldRMIimpl implements HelloWorldRMI {
  4. @Override
  5. public String sayHelloRmi(String msg) {
  6. System.out.println("================Server Side ========================");
  7. System.out.println("Inside Rmi IMPL - Incoming msg : " + msg);
  8. return "Hello " + msg + " :: Response time - > " + new Date();
  9. }
  10. }

3.3. Spring RMI 服务配置

在服务器项目中创建一个 spring bean 配置类,以使用RmiServiceExporter将接口和实现注册到 RMI 导出器。 我们需要提供它的端点名称以进行公开。 这将在基于 rmi 的 url(例如rmi://localhost:1099?)中公开实现类。

  • ServiceInterface – 我们需要提供要发布的接口的类。
  • Service – 这是服务实现类的实例。 为了简单起见,在这里我创建了一个新实例,但是我们可以在此处轻松给出我们要公开的另一个 spring bean。
  • ServiceName – 如上所述,这是我们要与 rmi url 中的服务关联的服务名称,客户端将使用该服务名称来调用服务。

Config.java

  1. package com.example.springrmiserver;
  2. import org.springframework.context.annotation.Bean;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.remoting.rmi.RmiServiceExporter;
  5. import org.springframework.remoting.support.RemoteExporter;
  6. import com.example.springrmiserver.service.HelloWorldRMI;
  7. import com.example.springrmiserver.service.HelloWorldRMIimpl;
  8. @Configuration
  9. public class Config {
  10. @Bean
  11. RemoteExporter registerRMIExporter() {
  12. RmiServiceExporter exporter = new RmiServiceExporter();
  13. exporter.setServiceName("helloworldrmi");
  14. exporter.setServiceInterface(HelloWorldRMI.class);
  15. exporter.setService(new HelloWorldRMIimpl());
  16. return exporter;
  17. }
  18. }

3.4. 构建和部署 RMI 服务

现在,我们可以完成最终构建并启动提供的 tomcat 服务器。 确保服务器正常启动,并记下特别是 RMI 端口的端口,该端口应为1099。 我们将在客户端使用此端口访问服务。

Console

  1. 2018-09-07 21:37:22.872 INFO 6452 --- [main] o.s.remoting.rmi.RmiServiceExporter
  2. : Binding service 'helloworldrmi' to RMI registry:
  3. RegistryImpl[UnicastServerRef [liveRef: [endpoint:[192.168.1.7:1099](docs_spring_local),objID:[0:0:0, 0]]]]

4. Spring 远程调用 – 创建 RMI 客户端

4.1. 在客户端项目中创建完全相同的服务接口

在客户端应用程序中创建相同的 rmi 接口。 这很重要,rmi 将使用此接口作为服务器端接口的框架,否则我们将获得异常。

4.2. Spring RMI 客户端配置

在客户端项目中配置RmiProxyFactoryBean bean 与远程服务连接。 这是所需的简单默认配置。

SpringRmiClientApplication.java

  1. package com.example.springrmiclient;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. import org.springframework.context.annotation.Bean;
  5. import org.springframework.remoting.rmi.RmiProxyFactoryBean;
  6. import com.example.springrmiserver.service.HelloWorldRMI;
  7. @SpringBootApplication
  8. public class SpringRmiClientApplication {
  9. @Bean
  10. RmiProxyFactoryBean rmiProxy() {
  11. RmiProxyFactoryBean bean = new RmiProxyFactoryBean();
  12. bean.setServiceInterface(HelloWorldRMI.class);
  13. bean.setServiceUrl("rmi://localhost:1099/helloworldrmi");
  14. return bean;
  15. }
  16. public static void main(String[] args)
  17. {
  18. HelloWorldRMI helloWorldRMI = SpringApplication.run(SpringRmiClientApplication.class, args)
  19. .getBean(HelloWorldRMI.class);
  20. System.out.println("================Client Side ========================");
  21. System.out.println(helloWorldRMI.sayHelloRmi("Sajal"));
  22. }
  23. }

4.3. Spring RMI 远程调用示例

现在,我们需要创建一个主要方法,在该方法中,我们将从 spring 上下文中查找实现接口 bean。 有了实例后,我们现在就可以像调用任何其他本地 java 方法一样调用服务器端方法。

如果一切顺利,将调用您的服务器端方法,检查服务器和客户端中的日志,该日志将可见。

  1. ================Server Side ========================
  2. Inside Rmi IMPL - Incoming msg : Sajal
  3. 2018-09-07 21:39:14.776 INFO Registering beans for JMX exposure on startup
  4. 2018-09-07 21:39:14.808 INFO Started SpringRmiClientApplication in 1.866 seconds (JVM running for 2.368)
  5. ================Client Side ========================
  6. Hello Sajal :: Response time - > Fri Sep 07 21:39:14 IST 2018

5. Spring Hessian 远程

Hessian 远程调用也与 RMI 远程调用非常相似。 唯一的变化是我们需要服务器端的 Hessian 相关服务导出器和客户端端的基于 Hessian 的代理 bean 工厂。

5.1. Hessian 的 Maven 依赖

pom.xml

  1. <dependency>
  2. <groupId>com.caucho</groupId>
  3. <artifactId>hessian</artifactId>
  4. <version>4.0.51</version>
  5. </dependency>

5.2. Hessian 的服务接口和实现

像 RMI 一样,我们将创建一对接口及其实现。 我们也可以重用 RMI,但是我在工作区中创建了一个新集。

HelloWorld.java

  1. package com.howtodoinjava.hessianserver.hessian;
  2. public interface HelloWorld {
  3. public String sayHelloWithHessian(String msg);
  4. }

HelloWorldImpl.java

  1. package com.howtodoinjava.hessianserver.hessian;
  2. import java.util.Date;
  3. public class HelloWorldImpl implements HelloWorld {
  4. @Override
  5. public String sayHelloWithHessian(String msg) {
  6. System.out.println("=============server side==============");
  7. System.out.println("msg : " + msg);
  8. return "Hello " + msg + " Response time :: " + new Date();
  9. }
  10. }

5.3. Hessian 服务器端配置 – 导出器

这是服务导出程序,它与 RMI 相同。 我刚刚更改了粗麻布相关的课程。

HessianConfiguration.java

  1. package com.howtodoinjava.hessianserver;
  2. import org.springframework.context.annotation.Bean;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.remoting.caucho.HessianServiceExporter;
  5. import org.springframework.remoting.support.RemoteExporter;
  6. import com.howtodoinjava.hessianserver.hessian.HelloWorld;
  7. import com.howtodoinjava.hessianserver.hessian.HelloWorldImpl;
  8. @Configuration
  9. public class HessianConfiguration {
  10. @Bean(name = "/hellohessian")
  11. RemoteExporter sayHelloServiceHessian() {
  12. HessianServiceExporter exporter = new HessianServiceExporter();
  13. exporter.setService(new HelloWorldImpl());
  14. exporter.setServiceInterface(HelloWorld.class);
  15. return exporter;
  16. }
  17. }

5.4. Hessian 的客户端配置 – 代理 Bean

此处的配置与 RMI 相同,但 URL 不同。Hessian 使用 HTTP 作为传输协议。 这是一个二进制协议,意味着所有数据都以二进制格式传输到服务器。 其余的复杂性由 Hessian 和 spring 处理。

HessianConfiguration.java

  1. package com.howtodoinjava.example.hessianclient;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. import org.springframework.context.ConfigurableApplicationContext;
  5. import org.springframework.context.annotation.Bean;
  6. import org.springframework.remoting.caucho.HessianProxyFactoryBean;
  7. import com.howtodoinjava.hessianserver.hessian.HelloWorld;
  8. @SpringBootApplication
  9. public class HessianClientApplication {
  10. @Bean
  11. public HessianProxyFactoryBean hessianInvoker() {
  12. HessianProxyFactoryBean invoker = new HessianProxyFactoryBean();
  13. invoker.setServiceUrl("http://localhost:8080/hellohessian");
  14. invoker.setServiceInterface(HelloWorld.class);
  15. return invoker;
  16. }
  17. public static void main(String[] args) {
  18. ConfigurableApplicationContext context = SpringApplication.run(HessianClientApplication.class, args);
  19. System.out.println("========Client Side===============");
  20. HelloWorld helloWorld = context.getBean(HelloWorld.class);
  21. System.out.println(helloWorld.sayHelloWithHessian("Sajal"));
  22. }
  23. }

5.5. Spring Hessian 远程调用示例

一旦运行服务器,就运行客户端应用程序。 我们应该看到已经调用了服务器端方法。

这是来自服务器和客户端的日志。

Server logs

  1. 2018-09-07 21:46:09.367 INFO 1264 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization started
  2. 2018-09-07 21:46:09.403 INFO 1264 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization completed in 36 ms
  3. =============server side==============
  4. msg : Sajal

Client logs

  1. 2018-09-07 21:46:08.903 INFO 6856 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
  2. 2018-09-07 21:46:09.197 INFO 6856 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 9090 (http) with context path ''
  3. 2018-09-07 21:46:09.205 INFO 6856 --- [ main] c.h.e.h.HessianClientApplication : Started HessianClientApplication in 6.069 seconds (JVM running for 7.092)
  4. ========Client Side===============
  5. Hello Sajal Response time :: Fri Sep 07 21:46:09 IST 2018

6. 总结

今天,我们已经看到了如何将远程方法公开为服务,以及如何使用不同的远程协议从客户端使用这些远程方法。 在此演示中,与网络服务不同,我们没有任何这样的载荷,在网络服务中,我们需要进行额外的处理以进行封送/编组。

通过这种方法,可以避免载荷处理的开销,但是需要注意的是,服务器和客户端都应仅是 Java,而不能从其他语言/运行时调用,例如 REST,我们可以轻松地从 JavaScript 调用。

牢记所有这些,我们可以选择所需的适当方法。

学习愉快!

参考文献:

Spring 5 远程调用文档

下载源码