Java RMI技术详解与案例分析

简介: 在实际的银行系统中,当然还需要考虑安全性、事务性、持久性以及错误处理等多方面的因素,RMI的网络通信也需要在安全的网络环境下进行,以防止数据泄露或被篡改。你在应用中是怎么使用 RMI 的,欢迎关注威哥爱编程,一起交流一下哈。

Java RMI(Remote Method Invocation)是一种允许Java虚拟机之间进行通信和交互的技术。它使得远程Java对象能够像本地对象一样被访问和操作,从而简化了分布式应用程序的开发。一些应用依然会使用 RMI 来实现通信和交互,今天的内容我们来聊聊 RMI 的那些事儿。

一、先来了解一下概念

RMI原理

RMI的基本思想是远程方法调用。客户端调用远程方法时,实际上是发送一个调用请求到服务器,由服务器执行该方法,并将结果返回给客户端。RMI通过存根(Stub)和骨架(Skeleton)类来实现远程调用,存根位于客户端,而骨架位于服务器端。

RMI组件

  1. 远程接口:必须继承自java.rmi.Remote接口,并声明抛出RemoteException
  2. 远程对象:实现了远程接口的类。
  3. RMI服务器:提供远程对象,并处理客户端的调用请求。
  4. RMI客户端:发起远程方法调用请求。
  5. 注册服务(Registry):提供服务注册与获取,类似于目录服务。

数据传递

RMI使用Java序列化机制来传递数据。客户端将方法参数序列化后通过网络发送给服务器,服务器反序列化参数并执行远程方法,然后将结果序列化回传给客户端。

RMI案例

以下是一个简单的RMI案例,包括服务器和客户端的实现思路,下文V 将再用代码来解释:

服务器端

  1. 实现一个远程接口,例如PersonController,包含一个远程方法queryName
  2. 创建该接口的具体实现类PersonControllerImpl,并在其中实现远程方法。
  3. 在服务器的main方法中,实例化远程对象,创建RMI注册表,并使用Naming.rebind将远程对象绑定到指定名称。

客户端

  1. 通过Naming.lookup方法,使用RMI注册表提供的名称获取远程对象的存根。
  2. 调用存根上的方法,就像调用本地方法一样,实际上是在调用服务器上的远程方法。

RMI的局限性

  • 语言限制:RMI是Java特有的技术,不能直接用于非Java应用程序。
  • 安全性问题:RMI的序列化机制可能带来安全风险,不建议将1099端口暴露在公网上。
  • 性能和扩展性:RMI的性能受网络延迟和带宽影响,且在高并发情况下可能面临扩展性限制。

RMI的应用场景

RMI适用于需要Java程序之间进行远程通信的场景,如分布式银行系统、游戏服务器、股票交易系统和网上商城等。接下来一起看一个简单的案例使用吧。

二、案例使用

先来搞一个简单的Java RMI服务器端和客户端的实现案例。这个案例中,服务器端将提供一个名为HelloWorld的远程服务,客户端将调用这个服务并打印返回的问候语。

服务器端实现

  1. 定义远程接口
    服务器和客户端都需要这个接口。它必须继承自java.rmi.Remote接口,并且所有远程方法都要声明抛出RemoteException
import java.rmi.Remote;
import java.rmi.RemoteException;

public interface HelloWorld extends Remote {
   
    String sayHello() throws RemoteException;
}
  1. 实现远程接口
    创建一个实现了上述接口的类,并实现远程方法。
import java.rmi.server.UnicastRemoteObject;
import java.rmi.RemoteException;

public class HelloWorldImpl extends UnicastRemoteObject implements HelloWorld {
   
    protected HelloWorldImpl() throws RemoteException {
   
        super();
    }

    @Override
    public String sayHello() throws RemoteException {
   
        return "Hello, World!";
    }
}
  1. 设置RMI服务器
    创建一个主类来设置RMI服务器,绑定远程对象到RMI注册表。
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;

public class HelloWorldServer {
   
    public static void main(String[] args) {
   
        try {
   
            // 创建远程对象
            HelloWorld helloWorld = new HelloWorldImpl();
            // 获取RMI注册表的引用,并在指定端口上创建或获取注册表实例
            LocateRegistry.createRegistry(1099);
            // 将远程对象绑定到RMI注册表中,客户端可以通过这个名字访问远程对象
            Naming.bind("rmi://localhost/HelloWorld", helloWorld);
            System.out.println("HelloWorld RMI object bound");
        } catch (Exception e) {
   
            System.err.println("Server exception: " + e.toString());
            e.printStackTrace();
        }
    }
}

客户端实现

  1. 调用远程服务
    客户端使用RMI注册表的名字来查找远程对象,并调用其方法。
import java.rmi.Naming;
import java.rmi.RemoteException;

public class HelloWorldClient {
   
    public static void main(String[] args) {
   
        try {
   
            // 使用RMI注册表的名字查找远程对象
            HelloWorld helloWorld = (HelloWorld) Naming.lookup("rmi://localhost/HelloWorld");
            // 调用远程方法
            String response = helloWorld.sayHello();
            System.out.println("Response: " + response);
        } catch (Exception e) {
   
            System.err.println("Client exception: " + e.toString());
            e.printStackTrace();
        }
    }
}

来详细解释吧

  • 远程接口 (HelloWorld): 这是服务器和客户端之间通信的协议。它定义了可以被远程调用的方法。
  • 远程对象实现 (HelloWorldImpl): 这是远程接口的一个实现。RMI调用实际上会调用这个实现中的方法。
  • 服务器 (HelloWorldServer): 负责创建远程对象的实例,并将这个实例绑定到RMI注册表中。这样客户端就可以通过注册表的名字来访问这个对象。
  • 客户端 (HelloWorldClient): 使用RMI注册表的名字来查找服务器上的远程对象,并调用其方法。

接下来就可以编译所有类文件,运行服务器端程序,确保RMI注册表已经启动(在某些Java版本中会自动启动),再运行客户端程序,搞定。注意一下哈,由于RMI使用Java序列化机制,因此客户端和服务器的类路径必须一致或兼容。

三、RMI 在分布式银行系统中的应用

接下来V哥要介绍业务场景下的应用了,拿在分布式银行系统中来说,我们可以使用RMI来实现不同银行分行之间的通信,例如,实现账户信息的查询、转账等操作。以下是一个简化的示例,其中包括两个基本操作:查询账户余额和执行转账,按步骤一步一步来吧。

步骤1: 定义远程接口

首先,定义一个远程接口BankService,它将被各个分行实现以提供银行服务。

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface BankService extends Remote {
   
    double getAccountBalance(String accountNumber) throws RemoteException;
    boolean transferFunds(String fromAccount, String toAccount, double amount) throws RemoteException;
}

步骤2: 实现远程接口

接下来,实现这个接口来创建远程对象,这个对象将提供实际的银行服务。

import java.rmi.server.UnicastRemoteObject;
import java.rmi.RemoteException;
import java.util.HashMap;
import java.util.Map;

public class BankServiceImpl extends UnicastRemoteObject implements BankService {
   
    private Map<String, Double> accounts = new HashMap<>();

    protected BankServiceImpl() throws RemoteException {
   
        super();
        // 初始化一些账户信息
        accounts.put("123456789", 5000.00);
        accounts.put("987654321", 1000.00);
    }

    @Override
    public double getAccountBalance(String accountNumber) throws RemoteException {
   
        return accounts.getOrDefault(accountNumber, 0.00);
    }

    @Override
    public boolean transferFunds(String fromAccount, String toAccount, double amount) throws RemoteException {
   
        if (accounts.containsKey(fromAccount) && accounts.get(fromAccount) >= amount) {
   
            accounts.put(fromAccount, accounts.get(fromAccount) - amount);
            accounts.merge(toAccount, amount, Double::sum);
            return true;
        }
        return false;
    }
}

步骤3: 设置RMI服务器

服务器端将创建BankService的远程对象实例,并将其绑定到RMI注册表中。

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

public class BankServer {
   
    public static void main(String[] args) {
   
        try {
   
            LocateRegistry.createRegistry(1099); // 创建RMI注册表
            BankService bankService = new BankServiceImpl();
            Naming.rebind("//localhost/BankService", bankService); // 绑定远程对象
            System.out.println("BankService is ready for use.");
        } catch (Exception e) {
   
            System.err.println("Server exception: " + e.toString());
            e.printStackTrace();
        }
    }
}

步骤4: 实现RMI客户端

客户端将使用RMI注册表的名字来查找远程对象,并调用其方法。

import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;

public class BankClient {
   
    public static void main(String[] args) {
   
        try {
   
            BankService bankService = (BankService) Naming.lookup("//localhost/BankService");
            System.out.println("Account balance: " + bankService.getAccountBalance("123456789"));

            // 执行转账操作
            boolean isTransferSuccess = bankService.transferFunds("123456789", "987654321", 200.00);
            if (isTransferSuccess) {
   
                System.out.println("Transfer successful.");
            } else {
   
                System.out.println("Transfer failed.");
            }

            // 再次查询余额
            System.out.println("New account balance: " + bankService.getAccountBalance("123456789"));
        } catch (RemoteException | NotBoundException e) {
   
            System.err.println("Client exception: " + e.toString());
            e.printStackTrace();
        }
    }
}

来详细解释一下

  • 远程接口 (BankService): 定义了两个方法:getAccountBalance用于查询账户余额,transferFunds用于执行转账操作。
  • 远程对象实现 (BankServiceImpl): 实现了BankService接口。它使用一个HashMap来模拟账户和余额信息。
  • 服务器 (BankServer): 设置了RMI服务器,将BankService的实现绑定到RMI注册表中,供客户端访问。
  • 客户端 (BankClient): 查找RMI注册表中的BankService服务,并调用其方法来查询余额和执行转账。

撸完代码后,编译所有类文件,运行服务器端程序BankServer,再运行客户端程序BankClient,测试效果吧。

最后

最后V哥要提醒一下,在实际的银行系统中,当然还需要考虑安全性、事务性、持久性以及错误处理等多方面的因素,RMI的网络通信也需要在安全的网络环境下进行,以防止数据泄露或被篡改。你在应用中是怎么使用 RMI 的,欢迎关注威哥爱编程,一起交流一下哈。

相关文章
|
23天前
|
JSON 前端开发 JavaScript
java-ajax技术详解!!!
本文介绍了Ajax技术及其工作原理,包括其核心XMLHttpRequest对象的属性和方法。Ajax通过异步通信技术,实现在不重新加载整个页面的情况下更新部分网页内容。文章还详细描述了使用原生JavaScript实现Ajax的基本步骤,以及利用jQuery简化Ajax操作的方法。最后,介绍了JSON作为轻量级数据交换格式在Ajax应用中的使用,包括Java中JSON与对象的相互转换。
38 1
|
26天前
|
jenkins Java 测试技术
如何使用 Jenkins 自动发布 Java 代码,通过一个电商公司后端服务的实际案例详细说明
本文介绍了如何使用 Jenkins 自动发布 Java 代码,通过一个电商公司后端服务的实际案例,详细说明了从 Jenkins 安装配置到自动构建、测试和部署的全流程。文中还提供了一个 Jenkinsfile 示例,并分享了实践经验,强调了版本控制、自动化测试等关键点的重要性。
60 3
|
28天前
|
SQL Java 数据库连接
在Java应用中,数据库访问常成为性能瓶颈。连接池技术通过预建立并复用数据库连接,有效减少连接开销,提升访问效率
在Java应用中,数据库访问常成为性能瓶颈。连接池技术通过预建立并复用数据库连接,有效减少连接开销,提升访问效率。本文介绍了连接池的工作原理、优势及实现方法,并提供了HikariCP的示例代码。
44 3
|
28天前
|
存储 Java 关系型数据库
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践,包括连接创建、分配、复用和释放等操作,并通过电商应用实例展示了如何选择合适的连接池库(如HikariCP)和配置参数,实现高效、稳定的数据库连接管理。
52 2
|
28天前
|
SQL 监控 Java
Java连接池技术的最新发展,包括高性能与低延迟、智能化管理与监控、扩展性与兼容性等方面
本文探讨了Java连接池技术的最新发展,包括高性能与低延迟、智能化管理与监控、扩展性与兼容性等方面。同时,结合最佳实践,介绍了如何选择合适的连接池库、合理配置参数、使用监控工具及优化数据库操作,以实现高效稳定的数据库访问。示例代码展示了如何使用HikariCP连接池。
15 2
|
28天前
|
Java 数据库连接 数据库
深入探讨Java连接池技术如何通过复用数据库连接、减少连接建立和断开的开销,从而显著提升系统性能
在Java应用开发中,数据库操作常成为性能瓶颈。本文通过问题解答形式,深入探讨Java连接池技术如何通过复用数据库连接、减少连接建立和断开的开销,从而显著提升系统性能。文章介绍了连接池的优势、选择和使用方法,以及优化配置的技巧。
27 1
|
28天前
|
算法 Java 数据库连接
Java连接池技术,从基础概念出发,解析了连接池的工作原理及其重要性
本文详细介绍了Java连接池技术,从基础概念出发,解析了连接池的工作原理及其重要性。连接池通过复用数据库连接,显著提升了应用的性能和稳定性。文章还展示了使用HikariCP连接池的示例代码,帮助读者更好地理解和应用这一技术。
40 1
|
29天前
|
Java 关系型数据库 数据库
面向对象设计原则在Java中的实现与案例分析
【10月更文挑战第25天】本文通过Java语言的具体实现和案例分析,详细介绍了面向对象设计的五大核心原则:单一职责原则、开闭原则、里氏替换原则、接口隔离原则和依赖倒置原则。这些原则帮助开发者构建更加灵活、可维护和可扩展的系统,不仅适用于Java,也适用于其他面向对象编程语言。
20 2
|
Java
Java中需要注意的一些案例
Java中需要注意的一些案例
119 0