Java RMI使用

Wesley13
• 阅读 714

什么是RMI

RMI,全称Remote Method Invoke,远程方法调用。它能够让在某个 Java 虚拟机上的对象调用另一个 Java 虚拟机中的对象上的方法。它的强大之处就体现在开发分布式网络应用的能力上,是纯Java的网络分布式应用系统的核心解决方案之一。它支持存储于不同地址空间的程序级对象之间彼此进行通信,实现远程对象之间的无缝远程调用。

RMI目前使用Java远程消息交换协议JRMP(Java Remote Messaging Protocol)进行通信。由于JRMP是专为Java对象制定的,Java RMI具有Java的"Write Once,Run Anywhere"的优点,是分布式应用系统的百分之百纯Java解决方案。用Java RMI开发的应用系统可以部署在任何支持JRE的平台上。但由于JRMP是专为Java对象制定的,因此,RMI对于用非Java语言开发的应用系统的支持不足。不能与用非Java语言书写的对象进行通信。

RMI可利用标准Java本机方法接口JNI与现有的和原有的系统相连接。RMI还可利用标准JDBC包与现有的关系数据库连接。RMI/JNI和RMI/JDBC相结合,可帮助您利用RMI与目前使用非Java语言的现有服务器进行通信,而且在您需要时可扩展Java在这些服务器上的使用。RMI可帮助您在扩展使用时充分利用Java的强大功能。

RMI的基础是接口,RMI构建基于一个重要的原理:定义接口和定义接口的具体实现是分开的。下面通过一个例子,演示如何使用Java RMI。

使用Java RMI

话不多说了,先看下项目结构图:

Java RMI使用

注意这里是两个项目,可以将Server与Client放在不同的机器上执行,我只有一台机器,所以只能将它们分成两个不同的项目了。

然后上代码(我也是一边学习一边测试,然后记录整个过程,因此这个接口就是大家见过的啦)

服务器端

1. 接口定义(RmiService.java):

package com.abc.rmi.service;

import java.rmi.Remote;

public interface RmiService extends Remote {
    public double add(double a,double b) throws java.rmi.RemoteException; 
    public double sub(double a,double b) throws java.rmi.RemoteException; 
    public double mul(double a,double b) throws java.rmi.RemoteException; 
    public double div(double a,double b) throws java.rmi.RemoteException;
    public double max(double a,double b) throws java.rmi.RemoteException;
    public double min(double a,double b) throws java.rmi.RemoteException;
    public double avg(double a,double b) throws java.rmi.RemoteException;
}

这个接口需要继承自Rmi的Remote接口。官方文档中这样说:

Any object that is a remote object must directly or indirectly implement this interface. Only those methods specified in a "remote interface", an interface that extends java.rmi.Remote are available remotely. 

注意,每一个方法声明都必须抛出java.rmi.RemoteException,否则在启动Server的时候会报以下错误

Rmi Server start failed: java.rmi.server.ExportException: remote object implements illegal remote interface; nested exception is: 
    java.lang.IllegalArgumentException: illegal remote method encountered: public abstract double com.abc.rmi.service.RmiService.add(double,double)

2.  接口实现(RmiServiceImpl.java):

package com.abc.rmi.service.impl;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import com.abc.rmi.service.RmiService;

public class RmiServiceImpl extends UnicastRemoteObject implements RmiService {
    private static final long serialVersionUID = -317455609230764324L;
    public RmiServiceImpl() throws RemoteException {
        super();
    }

    @Override
    public double add(double a, double b) throws RemoteException {
        return a + b;
    }
    @Override
    public double sub(double a, double b) throws RemoteException {
        return a - b;
    }
    @Override
    public double mul(double a, double b) throws RemoteException {
        return a * b;
    }
    @Override
    public double div(double a, double b) throws RemoteException {
        return a / b;
    }
    @Override
    public double max(double a, double b) throws RemoteException {
        return a > b ? a : b;
    }
    @Override
    public double min(double a, double b) throws RemoteException {
        return a < b ? a : b;
    }
    @Override
    public double avg(double a, double b) throws RemoteException {
        return (a + b) / 2;
    }
}

这个实现类使用了UnicastRemoteObject去联接RMI系统。这里我们继承了UnicastRemoteObject这个类它的作用是

Java RMI使用

事实上并不一定要这样做,如果一个类不是从UnicastRmeoteObject上继承,那必须使用它的exportObject()方法去联接到RMI。

如果一个类继承自UnicastRemoteObject,****那么它必须提供一个构造函数并且声明抛出一个RemoteException对象。当这个构造函数调用了super(),它将激活UnicastRemoteObject中的代码完成RMI的连接和远程对象的初始化。

3. 把方法注册到机器上(RmiServer.java):

package com.abc.rmi;

import java.rmi.Naming;
import java.rmi.registry.LocateRegistry;

import com.abc.rmi.service.impl.RmiServiceImpl;

/**
 * 启动 RMI 注册服务并进行对象注册
 */
public class RmiServer {
    private static final String RMI_HOST = "192.168.15.44";
    private static final int RMI_PORT = 6666;
    private static final String RMI_URL = "rmi://" + RMI_HOST + ":" + RMI_PORT + "/RmiServer";
    
    public static void main(String[] argv) {
        try {
            LocateRegistry.createRegistry(RMI_PORT);
            Naming.bind(RMI_URL, new RmiServiceImpl());
            System.out.println("Rmi Server[" + RMI_HOST + ":" + RMI_PORT + "] start success." );
        } catch (Exception e) {
            System.out.println("Rmi Server start failed: " + e);
        }
    }
}

到此为止,Server端的代码结束。

客户端

  1. 将服务器端的RmiService拷贝到项目结构图的位置。

2. 客户端调用(RmiClient.java):

package com.abc.rmi;

import java.rmi.Naming;
import com.abc.rmi.service.RmiService;

public class RmiClient {
    public static void main(String args[]) throws Exception {
        RmiService rmi = (RmiService) Naming.lookup("rmi://192.168.15.44:6666/RmiServer");
        System.out.println(rmi.add(100, 10));
        System.out.println(rmi.sub(100, 10));
        System.out.println(rmi.mul(100, 10));
        System.out.println(rmi.div(100, 10));
        System.out.println(rmi.max(100, 10));
        System.out.println(rmi.min(100, 10));
        System.out.println(rmi.avg(100, 10));
    }
}

注意这里的URL需要和Server端定义的一样。

运行测试

先运行RmiServer,

Java RMI使用

再运行RmiClient,看执行结果:

Java RMI使用

中间可能会遇到类似这样的错误:

Exception in thread “main” java.security.AccessControlException: access denied ( 
java.net.SocketPermission 192.168.15.44:6666 connect,resolve)

此时需要配置java.policy。打开%JAVA_HOME%/jre/lib/security/java.policy,添加这么一行:

permission java.net.SocketPermission "192.168.15.44:6666","listen,accept,connect,resolve";

保存后重启Server和Client即可。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
java 复制Map对象(深拷贝与浅拷贝)
java复制Map对象(深拷贝与浅拷贝)CreationTime2018年6月4日10点00分Author:Marydon1.深拷贝与浅拷贝  浅拷贝:只复制对象的引用,两个引用仍然指向同一个对象
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
李志宽 李志宽
1年前
【网络安全】为了2023年面试鹅厂渗透岗 死磕这几个知识
1、讲讲Java内存马原理和利用?Java内存马是一种通过在Java虚拟机(JVM)中运行的恶意代码,实现对被攻击者系统的远程控制。其原理是通过在Java虚拟机中注入特定的Java类、变量或方法等Java对象,然后在Java虚拟机中运行这些代码,实现对受害
Wesley13 Wesley13
3年前
Java日期时间API系列31
  时间戳是指格林威治时间1970年01月01日00时00分00秒起至现在的总毫秒数,是所有时间的基础,其他时间可以通过时间戳转换得到。Java中本来已经有相关获取时间戳的方法,Java8后增加新的类Instant等专用于处理时间戳问题。 1获取时间戳的方法和性能对比1.1获取时间戳方法Java8以前
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
3年前
RMI 相关知识
RMI是什么RMI全称是RemoteMethodInvocation-远程方法调用,JavaRMI在JDK1.1中实现的,其威力就体现在它强大的开发分布式网络应用的能力上,是纯Java的网络分布式应用系统的核心解决方案之一。其实它可以被看作是RPC的Java版本。但是传统RPC并不能很好地应用于分布式对象系统。而JavaRMI则支持存储
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Java服务总在半夜挂,背后的真相竟然是... | 京东云技术团队
最近有用户反馈测试环境Java服务总在凌晨00:00左右挂掉,用户反馈Java服务没有定时任务,也没有流量突增的情况,Jvm配置也合理,莫名其妙就挂了