深入分析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 原理

在这里插入图片描述
工作流程:

  1. When the client makes a call to the remote object, it is received by the stub which eventually passes this request to the RRL.
  2. 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.
  3. 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.
  4. 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接口,作为服务提供
(注意,我们会继承自UnicastRemoteObject 类,关于继承这个类的底层原理,我们可以参考:https://blog.51cto.com/guojuanjun/1423392

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章