JavaRmi远程方法调用

RMI使您可以以更高的抽象级别编程。它隐藏了套接字服务器,套接字,连接和发送或接收数据的详细信息。它甚至实现了引擎盖下的多线程服务器,而使用套接级编程,则必须明确地实现用于处理多个客户端的线程。 RMI应用程序是可扩展且易于维护的。您可以更改RMI服务器或将其移动到另一台计算机,而无需修改客户端程序,只是重置URL以找到服务器。 (为避免重置URL,您可以修改客户端将URL传递为命令行参数。)在套接级编程中,发送数据的客户端操作需要服务器操作来读取它。在套接字级别的客户端和服务器的实现是紧密同步的。 RMI客户端可以直接调用服务器方法,而插座级编程仅限于传递值。套接字级编程非常原始。避免使用它来开发客户端/服务器应用程序。作为一个类比,套接字级编程就像汇编语言的编程一样,而RMI编程就像以高级语言编程。

本地对象是只能在本地主机内访问。可从远程主机访问的对象称为 远程对象。对于要远程调用的对象,它必须在 Java 接口中定义 服务器和客户端都可以访问。此外,该接口必须扩展 java.lang.rmi.Remote 接口。像 java.io.Serializable 接口,java.rmi.Remote 是一个不包含常量或方法的标记接口。它仅用于识别远程 对象。

image-20211105171220449

image-20211106125338311

普通RMI 调用

客户端如何找到远程对象?RMI注册表提供了服务器的注册表服务,用于注册对象并为客户端找到对象。

您可以在Locateregistry类中使用几个超载的静态getRegisty()方法来返回对注册表的引用,

一旦获得了注册表,您可以使用绑定或Rebind方法在注册表中使用唯一名称绑定对象或使用查找方法找到对象

1
2
3
4
5
6
7
8
9
10
11
12
13
public:
Registry: 可在第三方启动,也可在Server启动, 区别是创建还是获取Registry注册中心
LocateRegistry.createRegistry(65532); //启动注册中心
CS共有一个接口
Server:
Registry registry = LocateRegistry.getRegistry("127.0.0.1", 65532);//获取注册中心
服务端特有接口的实现类,new一个出来以后包装成Remte对象
var proxy= UnicastRemoteObject.exportObject(simply, 1985);
bind/rebind/unbind/ 到key

Client:
Registry registry = LocateRegistry.getRegistry("127.0.0.1", 65532);
list/lookup key得到代理类转为接口,调用接口方法

image-20211107223557870

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//client
//获取到注册中心
Registry registry = LocateRegistry.getRegistry("127.0.0.1", 65532);
//搜索key-对应的 被代理的接口实现对象
var proxy = (Simple) registry.lookup("key");
//通过这个代理的接口实现对象 调用接口方法
System.out.println(proxy.say());
//-----------------------------------------
//server
//创建注册中心(也可以获取第三方注册中心)
var registry=LocateRegistry.createRegistry(65532);
//新建一个接口实现类
Simple simply=new SimpleRealizer();
//将这个实现类转换成代理类Remote类
var proxy= UnicastRemoteObject.exportObject(simply, 1985);
//去注册中心将上一步的代理接口类绑定key
registry.bind("key",proxy);
//-----------------------------------------
//接口
//继承Remote接口,方法必须抛异常
public interface Simple extends Remote {
String say() throws RemoteException;
}
//接口实现类
public class SimpleRealizer implements Simple {
@Override
public String say() throws RemoteException {
return "say:hello";
}
}


客户端调用 stub 的 say() 方法背后的流程:
client 端通过 stub 中包含的 host、port 信息,与 remote object 所在的 server 建立连接 ,然后序列化调用数据
server 端接收调用请求,将调用转发给 remote object,然后序列化结果,返回给 client
client 端接收、反序列化结果

注意事项

1
2
3
cs 共知接口 继承Remote 方法必须抛异常  throws RemoteException
(??此条待定)cs 注册的类必须同包名,不然找不到
c 参数 和 返回值,如果是Object ,必须手动实现序列化接口 implements Serializable

一句话总结

tips:正常 rmi 是 共知接口1,server有接口1的实现类,将实现类与key绑定,然后被client 获取并调用代理接口实现类的接口1中的已知方法

RMI回调

​ 传统Rmi只能是客户端调用服务端的方法,回调则提供了一种服务器端可以调用客户端方法的办法,具体如下:image-20211107223610637

1
2
3
4
5
6
7
8
9
10
public:
接口1和接口2 由 cs 共同知晓
c有接口2的实现类,s有接口1的实现类
接口1中有方法将接口2作为参数
C:
new接口2的实现类 I2 cl=new I2implenter();
拿到接口1的代理对象后 调用代理类的特殊传参(参数为接口2)方法
int result =proxy.pass(cl);
得到s端 接口2实现类 实现的方法返回值,也可能没有返回值
S: 无特殊
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//接口1
public interface I1 extends Remote {
String say() throws RemoteException;
int pass(I2 temp) throws RemoteException;
}
//-----------------------------------------
//接口1实现类(s端特有)
public class I1implenter implements I1{
@Override
public String say() throws RemoteException {
return "hello";
}
@Override
public int pass(I2 temp) throws RemoteException {
//此时可以自由使用接口2实现类的方法
//使用完后返回pass方法的返回值给client
return temp.sum(3,4);
}
}
//-----------------------------------------
//接口2
public interface I2 extends Remote {
int sum(int a,int b) throws RemoteException;
}
//-----------------------------------------
//接口2实现类(c端特有)
public class I2implenter implements I2, Serializable {
@Override
public int sum(int a, int b) throws RemoteException {
return a+b;
}
}
//-----------------------------------------
//client
Registry registry = LocateRegistry.getRegistry("127.0.0.1", 65532);
var proxy = (I1) registry.lookup("key");
System.out.println(proxy.say());
I2 cl=new I2implenter();
int result =proxy.pass(cl);
System.out.println("result="+result);
//server
var registry=LocateRegistry.createRegistry(65532);
I1 simply=new I1implenter();
var proxy= UnicastRemoteObject.exportObject(simply, 1985);
registry.bind("key",proxy);

注意事项

1
接口2实现类必须实现序列化接口
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2018-2022 ajian
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信