自己写了个Java RMI(远程方法调用)的实现案例
自己简单写了个Java RMI(远程方法调用)的实现案例。
为了更好理解RMI(远程方法调用)、序列化的意义等等,花费三天多的时间肝了一个Java RMI的实现案例。
!!!高能预警!!!
代码量有点大,先附上了简图用于理解
整个过程分为两大步
- 第一步--注册过程:客户端通过指定路由获取注册中心指定的远程客户端对象;
- 第二部--服务调用过程:客户端通过远程客户端对象访问远程服务端(代理服务)从而访问到真实服务的实现
调整为舒适的姿势,慢慢看…… 废话少说,上代码!!!
1.定义远程标记接口
面向接口编程,具体作用看后面的代码怎么使用
// 标记接口:直接或间接实现MyRMI接口将获得远程调用的能力
public interface MyRMI{
}
2.编写RMI 服务注册中心
注册中心类:用于注册服务和获取服务,核心是hashMap路由表对象
/**
* 注册中心:维护服务发布的注册表
*/
public class MyRMIRegistry {
// 默认端口
public final int REGISTRY_PORT = 10099;
private String host;
private int port;
private Map<String, MyRMI> bindings;
public MyRMIRegistry(int port){
this.port = port;
}
public MyRMIRegistry(String host, int port){
this.host=host;
this.port=port;
}
public void createRegistry(String serverName,MyRMI myRMI){
// 注册服务,并开启服务
this.bindings = new HashMap<>();
String host = null;
try {
host = Inet4Address.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
e.printStackTrace();
}
// 路由规则可自行定义,只要能确保Key唯一即可
String binding = "myrmi://"+host+":"+port+"/"+serverName;
this.bindings.put("myrmi://"+host+":"+port+"/"+serverName,myRMI);
System.out.println("注册的服务有:"+bindings.keySet().toString());
MyRMIRegistryServer myRMIRegistryServer = new MyRMIRegistryServer(this.port, this.bindings);
Executors.newCachedThreadPool().submit(myRMIRegistryServer); // 线程池启动服务
}
public MyRMI getRegistry(String serverName){
Socket socket = null;
ObjectOutputStream out = null;
ObjectInputStream in = null;
MyRMI myRMI = null;
// 通过
try {
socket = new Socket(host, port);
out = new ObjectOutputStream(socket.getOutputStream());
out.writeObject("myrmi://"+host+":"+port+"/"+serverName);
in = new ObjectInputStream(socket.getInputStream());
myRMI = (MyRMI)in.readObject();
} catch (IOException e) {
e.printStackTrace();
}catch (ClassNotFoundException e) {
e.printStackTrace();
}
return myRMI;
}
}
RMI 注册中心获取服务的线程:启动注册中心服务,等待客户端来获取路由表中的远程客户端
/**
* RMI注册中心获取服务线程
*/
public class MyRMIRegistryServer implements Runnable {
private int port;
private Map<String, MyRMI> bindings;
public MyRMIRegistryServer(Integer port,Map<String, MyRMI> bindings){
this.port = port;
this.bindings = bindings;
}
@Override
public void run() {
ServerSocket serverSocket = null;
ObjectOutputStream out = null;
ObjectInputStream in = null;
try {
serverSocket = new ServerSocket(this.port);
while(true){
Socket socket = serverSocket.accept();
in = new ObjectInputStream(socket.getInputStream());
out = new ObjectOutputStream(socket.getOutputStream());
// 看看客户端想要什么服务
String serverName = (String)in.readObject();
Iterator iterator = bindings.keySet().iterator();
while (iterator.hasNext()){
String key = (String) iterator.next();
if(serverName.equals(key)){
// 给客户端响应服务对象
MyRMI myRMI = bindings.get(key);
out.writeObject(myRMI);
}
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}finally {
// 异常后进入
try {
if (out!=null) out.close();
if (in!=null) in.close();
if (serverSocket!=null) serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
3.定义要发布的服务接口
需要提供RMI服务的接口,必须继承自定义的MyRMI标记接口
/**
* 服务接口
*/
public interface Hello extends MyRMI {
public String sayHello(String name);
}
4.服务用到的实体类
/**
* 对象数据类:Person
*/
public class Person implements Serializable {
// 序列化版本UID
private static final long serialVersionUID = 1L;
private String name;
private int age;
private String sex;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Person{" + "name='" + name + ", age=" + age + ", sex='" + sex + '}';
}
public Person() {
}
public Person(String name, Integer age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
}
5.实现要发布的服务接口
/**
* 对外提供的服务实现
*/
public class HelloImpl implements Hello {
private static File file = new File("D:/HelloRMI.txt");
private static List<Person> list = new ArrayList<>();
@Override
public String sayHello(String name) {
String result = "没有获取到"+name+"的信息";
try {
List<Person> personList = readList();
for(Person person:personList){
if (person.getName().equals(name)){
result = "Hello , welcome to the RMI! "
+ "姓名:"+name + " 年龄:"+person.getAge()+" 性别:"+person.getSex();
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return result;
}
/**
* 生成数据,为测试做准备
* @param args
* @throws IOException
* @throws ClassNotFoundException
*/
public static void main(String[] args) throws IOException, ClassNotFoundException {
//数据准备:集合类都实现了序列化接口Serializable
list.add(new Person("张三", 38, "男"));
list.add(new Person("李四", 38, "男"));
list.add(new Person("如花", 18, "女"));
// 持久化对象数据
writerList(list);
// 查询持久化对象数据
List<Person> personList = readList();
System.out.println("遍历持久化对象数据>");
for (Person person : personList) {
System.out.println(person);
if (person.getAge() == 38) {
person.setAge(18);
}
}
}
public static void writerList(List<Person> list) throws IOException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(file));
objectOutputStream.writeObject(list);
objectOutputStream.close();
}
public static List<Person> readList() throws IOException, ClassNotFoundException {
// 读取普通文件反序列化
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(file));
List<Person> personList = (List<Person>) objectInputStream.readObject();
objectInputStream.close();
return personList;
}
}
6.远程客户端的线程类
用于自动生成服务接口(继承了MyRMI标记接口)的远程客户端类:这个类原本是通用类实现,为了方便实现,就直接实现Hello接口了
/**
* 远程客户端的线程类的生成:
* 为了方便实现,这边直接实现服务接口编写
*/
public class HelloClientThread implements Hello,Serializable {
// 序列化版本UID
private static final long serialVersionUID = 1L;
private Map<String,Object> map = new HashMap<>(); // 报文对象:方法名和参数对象
private String ip;
private int port;
public HelloClientThread(String ip, int port){
this.ip = ip;
this.port = port;
}
@Override
public String sayHello(String name) {
map.put("sayHello",name);
String result = (String)send();
return result;
}
private Object send(){
Object o =null;
Socket socket = null;
ObjectOutputStream out = null;
ObjectInputStream in = null;
try {
socket = new Socket(ip, port);
out = new ObjectOutputStream(socket.getOutputStream());
in = new ObjectInputStream(socket.getInputStream());
// 告诉服务端我要调用什么服务
out.writeObject(map);
// 获取服务实现对象
o = in.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}finally {
try {
if (out!=null) out.close();
if (in!=null) in.close();
if (socket!=null) socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return o;
}
}
7.远程服务端的线程类
用于自动生成服务接口(继承了MyRMI标记接口)的远程服务端类:这个类原本也是通用类实现,为了方便实现,部分代码尚未做到解耦通用
/**
* 远程服务端的线程类的生成:
* 为了方便实现,这边直接实现服务线程类
*/
public class HelloServerThread implements Runnable {
private Integer port;
private MyRMI myRMI;
public HelloServerThread(Integer port, MyRMI myRMI){
this.port = port;
this.myRMI = myRMI;
}
@Override
public void run() {
ServerSocket serverSocket = null;
ObjectOutputStream out = null;
ObjectInputStream in = null;
try {
serverSocket = new ServerSocket(this.port);
while(true){
Socket socket = serverSocket.accept();
in = new ObjectInputStream(socket.getInputStream());
out = new ObjectOutputStream(socket.getOutputStream());
// 看看客户端想要什么服务
Map map = (Map)in.readObject();
Iterator iterator = map.keySet().iterator();
while (iterator.hasNext()){
String key = (String) iterator.next();
if("sayHello".equals(key)){
// 给客户端响应服务对象
Hello hello = (Hello)myRMI;
String result = hello.sayHello((String) map.get(key));
out.writeObject(result);
}
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}finally {
// 异常后进入
try {
if (out!=null) out.close();
if (in!=null) in.close();
if (serverSocket!=null) serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
8.远程客户端生成和远程服务端生成和启动的类
/**
* 远程客户端生成和远程服务端生成和启动的类
*/
public class RemoteSocketObject{
// 默认端口
private int port=18999;
// 指定远程通讯端口和代理服务
public MyRMI createRemoteClient(MyRMI myRMI,int port){
if (port > 0)
this.port=port;
MyRMI myRMIClient = null;
try {
// 生成底层通讯服务端,并启动
HelloServerThread helloServerThread = new HelloServerThread(this.port, myRMI);
Executors.newCachedThreadPool().submit(helloServerThread); // 线程池启动服务
// 生成底层通讯客户端
String localHost = Inet4Address.getLocalHost().getHostAddress();
System.out.println("host="+localHost+",port="+this.port);
myRMIClient= new HelloClientThread(localHost, this.port);
} catch (Exception e) {
e.printStackTrace();
}
return myRMIClient;
}
}
9.服务发布类
/**
* RMI 服务发布类
*/
public class HelloServer {
public static void main(String[] args) {
System.out.println("Create Hello Remote Method Invocation...");
// 实例化一个Hello
Hello hello = new HelloImpl();
// 转换成远程服务,并提供远程客户端
Hello remoteClient = (Hello)new RemoteSocketObject().createRemoteClient(hello, 0);
// 将服务实现托管到Socket服务
MyRMIRegistry myRMIRegistry = new MyRMIRegistry(16000);
// 开启线程服务
myRMIRegistry.createRegistry("Hello",remoteClient);
}
}
10.客户端测试类
/**
* 客户端测试类
* 客户端只知道服务接口、服务发布的地址和服务发布的名称
*/
public class TestHello {
public static void main(String[] args) {
// 注意不是127.0.0.1,不知道host的看server端启动后打印的信息
// 端口16000是注册中心的端口,底层代理服务的端口客户端无需知道
MyRMIRegistry client = new MyRMIRegistry("192.168.233.1", 16000);
Hello hello = (Hello) client.getRegistry("Hello");
System.out.println(hello.sayHello("张三"));
}
}
11.总结
所有代码整下来,在真正的场景中:
客户端只知道:TestHello类、Hello接口定义、MyRMI标记接口、MyRMIRegistry注册类代码(路由表中只知道Key,不知道具体值);
服务端只知道:Hello接口、HelloImpl服务实现类、MyRMI标记接口、MyRMIRegistry注册类代码(路由表中知道Key和具体值);
关于其他的代码实现都是无感的,为了简单实现远程客户端和远程服务端,将服务接口耦合到两者上了,未做到解耦通用。
更多优质文章和资源
原创不易、三联支持:分享,点赞,在看
自己写了个Java RMI(远程方法调用)的实现案例的更多相关文章
- JAVA RMI远程方法调用简单实例[转]
RMI的概念 RMI(Remote Method Invocation)远程方法调用是一种计算机之间利用远程对象互相调用实现双方通讯的一种通讯机制.使用这种机制,某一台计算机上的对象可以调用另外 一台 ...
- JAVA RMI远程方法调用简单实例(转载)
来源:http://www.cnblogs.com/leslies2/archive/2011/05/20/2051844.html RMI的概念 RMI(Remote Method Invocati ...
- Java RMI(远程方法调用) 实例与分析 (转)
目的: 通过本文,可以加深对Java RMI的理解,知道它的工作原理,怎么使用等. 也为了加深我自己的理解,故整理成文.不足之处,还望指出. 概念解释: RMI(RemoteMethodInvocat ...
- Java RMI(远程方法调用) 实例与分析
目的: 通过本文,可以加深对Java RMI的理解,知道它的工作原理,怎么使用等. 也为了加深我自己的理解,故整理成文.不足之处,还望指出. 概念解释: RMI(RemoteMethodInvocat ...
- java rmi远程方法调用实例
RMI的概念 RMI(Remote Method Invocation)远程方法调用是一种计算机之间利用远程对象互相调用实现双方通讯的一种通讯机制.使用这种机制,某一台计算机上的对象可以调用另外一台计 ...
- Java RMI之HelloWorld经典入门案例
Java RMI 指的是远程方法调用 (Remote Method Invocation).它是一种机制,能够让在某个 Java 虚拟机上的对象调用另一个 Java 虚拟机中的对象上的方法.可以用此方 ...
- Java RMI 框架_远程方法调用(2016-08-16)
概念: Java RMI 指的是远程方法调用 (Remote Method Invocation).它是一种机制,能够让在某个 Java 虚拟机上的对象调用另一个 Java 虚拟机中的对象上的方法.可 ...
- Java的RMI远程方法调用实现和应用
最近在学习Dubbo,RMI是很重要的底层机制,RMI(Remote Method Invocation)远程方法调用是一种计算机之间利用远程对象互相调用实现双方通讯的一种通讯机制.使用这种机制,某一 ...
- Java RMI(远程方法调用)开发
参考 https://docs.oracle.com/javase/7/docs/platform/rmi/spec/rmi-arch2.html http://www.cnblogs.com/wxi ...
随机推荐
- Python解释器下载安装教程
简介: 自从20世纪90年代初Python语言诞生至今,它已被逐渐广泛应用于系统管理任务的处理和Web编程.2021年10月,语言流行指数的编译器Tiobe将Python加冕为最受欢迎的编程语言,20 ...
- IDEA出现Cannot resolve symbol “xxx“(无法解析符号)的解决办法
1,File->Invalidate Caches/Restart 清除缓存并重启 idea 2,检查pom文件中的依赖关系是否正确 3,maven -> Reimport 4,打开pro ...
- plsql 普通游标
-- ①普通游标:操作步骤 边敲边想 学无止境 /* 游标:指针:内存中的一块数据缓冲区,上下文: 将查询到的结果集缓存起来,便于进行逐行定位处理. 使用完成后要及时关闭: 操作步骤? 1.定义游标 ...
- 深入Windows APC
本篇原文为 Depths of Windows APC ,如果有良好的英文基础,可以点击该链接进行阅读.本文为我个人:寂静的羽夏(wingsummer) 中文翻译,非机翻,著作权归原作者 Rbmm ...
- Mysql 返回JSON值属性的函数 (五)
本节中的函数返回JSON值的属性. JSON_DEPTH(json_doc) 返回JSON文档的最大深度.NULL如果参数为,则 返回 NULL.如果参数不是有效的JSON文档,则会发生错误. 一个空 ...
- VC 常用
转载请注明来源:https://www.cnblogs.com/hookjc/ ------------------------------------------------------------ ...
- AttributeText创建多彩文字Label --- hl
一般用富文本实现多种花样的Label文字,下图是利用UILabel分类快速创建的多彩多样lable文字,快速简单,自定义性强,更重要的是无代码污染,特别适合轻量级使用 https://github.c ...
- JS IndexOf移除符合规则的一项
RemoveItem: function (val) { var index = selectedUsers.indexOf(val); if (index > -1) { selectedUs ...
- python基础语法_8循环语句
http://www.runoob.com/python3/python3-loop.html while 循环 Python中while语句的一般形式: while 判断条件: 语句 无限循环 我们 ...
- 20161206日更新CocoaPods版本
从网上下载的工程第三方库需要更新,但当我执行pod update时提示以下错误: [!] The `master` repo requires CocoaPods 1.0.0 - (currentl ...