Java-RMI详解
1 介绍
java本身的内部中是提供了一种RPC框架-RMI(即Remote Method Invoke,远程方法调用),位于rt.jar包中,以java.rmi开头的package中。
它是一种允许一个对象驻留在一个系统(JVM)来访问/调用一个物体在另一个JVM中运行的机制
2 机制
2.1 内部体系架构
在一个RMI 的程序中,我们需要提供两个模块,一个是server模块(用于提供服务),一个是client模块(用于调度服务)
- server模块中,我们需要使用注册表来获取本地主机的远程注册表实例,并将需要系统的远程服务绑定到注册表上去
- client模块中,我们需要获取服务器上的远程对象,并视图调用它
内部调用结构图如下:
Transport Layer(传输层):传输层用于连接客户端和服务断,它用于管理现有的连接和设置新连接
Stud(存根):Stud位于client端,它作为一个网关(gateway)用于代理远程调度对象
Skeleton(骨架):Skeleton位于server端,它用于连接stud,用于接收调用server端的请求。
RRL(Remote Reference Layer)(远程引用层):管理client和server端的引用
2.2 原理
工作流程:
- When the client makes a call to the remote object, it is received by the stub which eventually passes this request to the RRL.
- When the client-side RRL receives the request, it invokes a method called invoke() of the object remoteRef. It passes the request to the RRL on the server side.
- The RRL on the server side passes the request to the Skeleton (proxy on the server) which finally invokes the required object on the server.
- The result is passed all the way back to the client.
3 代码示例
RMI在编写接口作为调度服务的时候,需要继承自Remote,Remote用于标识其方法可以从非本地虚拟机上调用的接口,只有在远程接口中指定这些方法才可以远程调用
并且由于网络的原因,我们需要在声明远程调度的方法中主动抛出RemoteException异常
public interface Hello extends Remote{
String printMsg() throws RemoteException;
}
实现Hello接口,作为服务提供
public class ImplExample extends UnicastRemoteObject implements Hello{
public ImplExample() throws RemoteException {
super();
}
@Override
public String printMsg() throws RemoteException {
System.out.println("This is an example RMI program");
return "ZHOUCG";
}
}
编写 Server端,(在这里我们通过命名空间Naming的方式发布服务)
public class NamingService {
public static void main(String[] args) throws RemoteException, AlreadyBoundException, MalformedURLException {
// 本地主机上的远程对象注册表Registry的实例
LocateRegistry.createRegistry(1100);
// 创建一个远程对象
Hello hello = new ImplExample();
// 把远程对象注册到RMI注册服务器上,并命名为Hello
// 绑定的URL标准格式为:rmi://host:port/name
Naming.bind("rmi://localhost:1100/HelloNaming",hello);
System.out.println("======= 启动RMI服务成功! =======");
}
}
编写client端,调用服务:
public class NamingClient {
public static void main(String[] args) throws RemoteException, NotBoundException, MalformedURLException {
String remoteAddr = "rmi://localhost:1100/HelloNaming";
Hello hello = (Hello) Naming.lookup(remoteAddr);
String response = hello.printMsg();
System.out.println("=======> " + response + " <=======");
}
}
启动Server和client,输出结果:
Server端:
Client端:
上面的示例,是Java RMI的基本示例。
在spring源码中,spring同样提供了对于RMI代码的封装,我们可以很方便的在spring中使用RMI,代码位于spring-context包下的org.springframework.remoting package 中
定义RMI服务端接口:
public interface Hello extends Remote{
String printMsg(String msg) throws RemoteException;
}
对于Server端的服务实现类:
@Service
public class ImpHello implements Hello {
public String printMsg(String msg) throws RemoteException {
System.out.println("RMI server get request msg:"+msg);
return "ZHOUCG"+msg;
}
}
定义Server端的服务配置:
@Configuration
public class RmiServiceConfig {
@Bean
public RmiServiceExporter registerService(Hello hello) {
RmiServiceExporter rmiServiceExporter = new RmiServiceExporter();
rmiServiceExporter.setServiceName("hello");
rmiServiceExporter.setService(hello);
rmiServiceExporter.setServiceInterface(Hello.class);
rmiServiceExporter.setRegistryPort(1101);
return rmiServiceExporter;
}
}
客户端调用:
RMI客户端服务配置:
@Configuration
public class RmiClientConfig {
@Bean
public Hello userInfo() {
RmiProxyFactoryBean rmiProxyFactoryBean = new RmiProxyFactoryBean();
rmiProxyFactoryBean.setServiceUrl("rmi://localhost:1101/hello");
rmiProxyFactoryBean.setServiceInterface(Hello.class);
rmiProxyFactoryBean.afterPropertiesSet();
return (Hello) rmiProxyFactoryBean.getObject();
}
}
服务测试:
@RestController
@RequestMapping("/hello")
public class TestController {
@Autowired
private Hello hello;
@GetMapping
public String test() throws RemoteException {
String wl = hello.printMsg("WL");
return wl;
}
}
4 参考
https://www.tutorialspoint.com/java_rmi/java_rmi_application.htm
上一篇: 美团(Leaf)分布式ID算法(实战)
下一篇: 分布式Session解决方案