原文: https://howtodoinjava.com/spring-boot2/spring-remoting-rmi-hessian/
在此 Spring boot 2 rmi 示例中,了解 spring 如何提供不同的机制来调用驻留在不同 JVM 中并且很可能在不同服务器中的远程方法。 这称为 RMI(远程方法调用)概念。
RMI 出现在 EJB 的早期。 在当今的 HTTP / HTTPS 世界中,SOAP 和 REST 在实现任何服务中都占主导地位,但是在 Spring 框架中,远程调用仍然是一种选择。 今天,我们将在这个领域看到两个新协议,主要是 Hessian 和 RMI 。
Spring 框架提供了一系列工具,这些工具统称为 Spring 远程调用。 这些工具使我们可以在远程系统上调用远程服务,就像它们在本地可用一样。
通常不需要,但是在某些情况下,我们仍然倾向于使用远程调用。 使用远程调用的主要原因是性能和安全性。 基于 HTTP 的服务会影响性能,因为我们必须处理请求和响应(封送/拆组)。 更重要的是,如果仅使用这些技术就已经将服务器公开为远程服务,则客户端除了 RMI 别无选择。
以下主要是 spring 帮助我们实现远程调用的方法。
-
远程方法调用(RMI) – 在 Spring,通过在服务器端使用
RmiServiceExporter和在客户端使用RmiProxyFactoryBean实现了 RMI。 -
Spring 的 HTTP 调用程序 – 这是通过使用
HttpInvokerProxyFactoryBean和HttpInvokerServiceExporter进行远程调用的另一种方式。 这基本上是通过 HTTP 的 Java 序列化,支持任何 Java 接口。 -
Hessian – 它是 Caucho 提供的基于 HTTP 的轻量级二进制协议。 我们可以使用 Hessian Spring 的
HessianProxyFactoryBean和HessianServiceExporter的 Spring 包装器来公开和使用远程服务。 内部在传输时使用二进制数据 -
Burlap – Burlap 是用于远程处理的基于 XML 的协议。 它也是由 Caucho 开发的。 它类似于 Hessian,唯一的区别是 Hessian 是二进制,而 Burlap 是 XML。 与 Hessian 一样,Burlap 需要通过 HTTP 托管。与 Hessian 相似,它具有
BurlapServiceExporter和BurlapProxyFactoryBean类。请注意,由于尚未积极开发 Burlap,自 Spring 4.0 起就不再支持它。
如上所述,我们将使用 Spring Boot 为 RMI 和 Hessian 开发项目。 我们将创建两个 Maven 项目。 服务器的第一个项目是托管实现,另一个是客户端部分。 客户端应用程序将通过相应的远程协议与服务器通信,以调用远程方法。
创建两个名为spring-rmi-server和spring-rmi-client的简单的 Spring Boot 项目,它们具有spring-boot-web依赖,以将其托管在 Web 服务器中。
为此,我们需要转到 https://start.spring.io/ 并提供 Maven 坐标并选择依赖项。 下载包含框架项目的 zip 文件。 然后,一旦解压缩到合适的文件夹中,我们就需要将其导入 eclipse 中。
客户端项目中不需要 Web 依赖项,因为我们将其作为独立的应用程序运行。
Spring 项目创建
编写我们要公开的 Java 接口和 RMI 服务的实现。 在本教程中,我们将创建一个简单的 hello world 应用程序,该应用程序将根据输入向用户打招呼。 这是必需的接口及其实现。
HelloWorldRMI.java
package com.example.springrmiserver.service;
public interface HelloWorldRMI
{
public String sayHelloRmi(String msg);
}HelloWorldRMIimpl.java
package com.example.springrmiserver.service;
import java.util.Date;
public class HelloWorldRMIimpl implements HelloWorldRMI {
@Override
public String sayHelloRmi(String msg) {
System.out.println("================Server Side ========================");
System.out.println("Inside Rmi IMPL - Incoming msg : " + msg);
return "Hello " + msg + " :: Response time - > " + new Date();
}
}在服务器项目中创建一个 spring bean 配置类,以使用RmiServiceExporter将接口和实现注册到 RMI 导出器。 我们需要提供它的端点名称以进行公开。 这将在基于 rmi 的 url(例如rmi://localhost:1099?)中公开实现类。
ServiceInterface– 我们需要提供要发布的接口的类。Service– 这是服务实现类的实例。 为了简单起见,在这里我创建了一个新实例,但是我们可以在此处轻松给出我们要公开的另一个 spring bean。ServiceName– 如上所述,这是我们要与 rmi url 中的服务关联的服务名称,客户端将使用该服务名称来调用服务。
Config.java
package com.example.springrmiserver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.remoting.rmi.RmiServiceExporter;
import org.springframework.remoting.support.RemoteExporter;
import com.example.springrmiserver.service.HelloWorldRMI;
import com.example.springrmiserver.service.HelloWorldRMIimpl;
@Configuration
public class Config {
@Bean
RemoteExporter registerRMIExporter() {
RmiServiceExporter exporter = new RmiServiceExporter();
exporter.setServiceName("helloworldrmi");
exporter.setServiceInterface(HelloWorldRMI.class);
exporter.setService(new HelloWorldRMIimpl());
return exporter;
}
}现在,我们可以完成最终构建并启动提供的 tomcat 服务器。 确保服务器正常启动,并记下特别是 RMI 端口的端口,该端口应为1099。 我们将在客户端使用此端口访问服务。
Console
2018-09-07 21:37:22.872 INFO 6452 --- [main] o.s.remoting.rmi.RmiServiceExporter
: Binding service 'helloworldrmi' to RMI registry:
RegistryImpl[UnicastServerRef [liveRef: [endpoint:[192.168.1.7:1099](local),objID:[0:0:0, 0]]]]在客户端应用程序中创建相同的 rmi 接口。 这很重要,rmi 将使用此接口作为服务器端接口的框架,否则我们将获得异常。
在客户端项目中配置RmiProxyFactoryBean bean 与远程服务连接。 这是所需的简单默认配置。
SpringRmiClientApplication.java
package com.example.springrmiclient;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.remoting.rmi.RmiProxyFactoryBean;
import com.example.springrmiserver.service.HelloWorldRMI;
@SpringBootApplication
public class SpringRmiClientApplication {
@Bean
RmiProxyFactoryBean rmiProxy() {
RmiProxyFactoryBean bean = new RmiProxyFactoryBean();
bean.setServiceInterface(HelloWorldRMI.class);
bean.setServiceUrl("rmi://localhost:1099/helloworldrmi");
return bean;
}
public static void main(String[] args)
{
HelloWorldRMI helloWorldRMI = SpringApplication.run(SpringRmiClientApplication.class, args)
.getBean(HelloWorldRMI.class);
System.out.println("================Client Side ========================");
System.out.println(helloWorldRMI.sayHelloRmi("Sajal"));
}
}现在,我们需要创建一个主要方法,在该方法中,我们将从 spring 上下文中查找实现接口 bean。 有了实例后,我们现在就可以像调用任何其他本地 java 方法一样调用服务器端方法。
如果一切顺利,将调用您的服务器端方法,检查服务器和客户端中的日志,该日志将可见。
================Server Side ========================
Inside Rmi IMPL - Incoming msg : Sajal
2018-09-07 21:39:14.776 INFO Registering beans for JMX exposure on startup
2018-09-07 21:39:14.808 INFO Started SpringRmiClientApplication in 1.866 seconds (JVM running for 2.368)
================Client Side ========================
Hello Sajal :: Response time - > Fri Sep 07 21:39:14 IST 2018Hessian 远程调用也与 RMI 远程调用非常相似。 唯一的变化是我们需要服务器端的 Hessian 相关服务导出器和客户端端的基于 Hessian 的代理 bean 工厂。
pom.xml
<dependency>
<groupId>com.caucho</groupId>
<artifactId>hessian</artifactId>
<version>4.0.51</version>
</dependency> 像 RMI 一样,我们将创建一对接口及其实现。 我们也可以重用 RMI,但是我在工作区中创建了一个新集。
HelloWorld.java
package com.howtodoinjava.hessianserver.hessian;
public interface HelloWorld {
public String sayHelloWithHessian(String msg);
}HelloWorldImpl.java
package com.howtodoinjava.hessianserver.hessian;
import java.util.Date;
public class HelloWorldImpl implements HelloWorld {
@Override
public String sayHelloWithHessian(String msg) {
System.out.println("=============server side==============");
System.out.println("msg : " + msg);
return "Hello " + msg + " Response time :: " + new Date();
}
}这是服务导出程序,它与 RMI 相同。 我刚刚更改了粗麻布相关的课程。
HessianConfiguration.java
package com.howtodoinjava.hessianserver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.remoting.caucho.HessianServiceExporter;
import org.springframework.remoting.support.RemoteExporter;
import com.howtodoinjava.hessianserver.hessian.HelloWorld;
import com.howtodoinjava.hessianserver.hessian.HelloWorldImpl;
@Configuration
public class HessianConfiguration {
@Bean(name = "/hellohessian")
RemoteExporter sayHelloServiceHessian() {
HessianServiceExporter exporter = new HessianServiceExporter();
exporter.setService(new HelloWorldImpl());
exporter.setServiceInterface(HelloWorld.class);
return exporter;
}
}此处的配置与 RMI 相同,但 URL 不同。Hessian 使用 HTTP 作为传输协议。 这是一个二进制协议,意味着所有数据都以二进制格式传输到服务器。 其余的复杂性由 Hessian 和 spring 处理。
HessianConfiguration.java
package com.howtodoinjava.example.hessianclient;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.remoting.caucho.HessianProxyFactoryBean;
import com.howtodoinjava.hessianserver.hessian.HelloWorld;
@SpringBootApplication
public class HessianClientApplication {
@Bean
public HessianProxyFactoryBean hessianInvoker() {
HessianProxyFactoryBean invoker = new HessianProxyFactoryBean();
invoker.setServiceUrl("http://localhost:8080/hellohessian");
invoker.setServiceInterface(HelloWorld.class);
return invoker;
}
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(HessianClientApplication.class, args);
System.out.println("========Client Side===============");
HelloWorld helloWorld = context.getBean(HelloWorld.class);
System.out.println(helloWorld.sayHelloWithHessian("Sajal"));
}
}一旦运行服务器,就运行客户端应用程序。 我们应该看到已经调用了服务器端方法。
这是来自服务器和客户端的日志。
Server logs
2018-09-07 21:46:09.367 INFO 1264 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization started
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
=============server side==============
msg : SajalClient logs
2018-09-07 21:46:08.903 INFO 6856 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
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 ''
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)
========Client Side===============
Hello Sajal Response time :: Fri Sep 07 21:46:09 IST 2018今天,我们已经看到了如何将远程方法公开为服务,以及如何使用不同的远程协议从客户端使用这些远程方法。 在此演示中,与网络服务不同,我们没有任何这样的载荷,在网络服务中,我们需要进行额外的处理以进行封送/编组。
通过这种方法,可以避免载荷处理的开销,但是需要注意的是,服务器和客户端都应仅是 Java,而不能从其他语言/运行时调用,例如 REST,我们可以轻松地从 JavaScript 调用。
牢记所有这些,我们可以选择所需的适当方法。
学习愉快!
参考文献:
