远程方法调用RMI初步

本文以一个简单的例子介绍开发Remote Method Invocation/RMI应用程序的完整流程。

作为学习《编程导论(Java)·3.1.3 接口与实现分离》和《4.3 Java接口》的补充资料;

作为学习Android 远程服务的背景知识。


设想有这样一个情景:网络上存在一个基于Java的计算引擎,该服务器为我们/客户端执行某个(例如计算密集型的)Java程序。客户端能够调用某个巨型计算机/远程服务器上的远程对象的远程方法,而且如同调用本地对象的方法一样方便自然,这是一个引人注目的、令人开心的事情。

操作分布即把计算分散给不同主机进行处理、或者说 分布式对象计算[distributed object computing]是一个重要的编程领域,最典型而简单的实现方式就是 远程方法调用[Remote Method Invocation, RMI]

本节以一个简单的例子介绍开发RMI应用程序的完整流程。RMI应用程序将由3部分构成。

  1. 远程服务接口。它是服务器-客户端之间的协议,它将被打包发布给服务器、客户端程序员。
  2. 服务器编程:实现远程服务接口、启动服务器
  3. 客户端编程:调用远程对象的远程方法。

7.1.1定义远程接口

在各层面的网络编程中,会接触到各种协议(protocol)。网络协议(network protocol、简称协议)是数据格式的形式化描述和交换那些数据的规则集。大多数典型的应用都有了标准化的网络协议,如FTP、HTTP、Internet Protocol (互联网协议、IP)等等。

在面向对象程序中,协议或接口指客户(clinet)或客户程序所需要知道的所有信息,包括方法头(方法原型)、和非常重要的说明文档。

RMI应用程序遵循服务器-客户端模型。因此服务器-客户端之间的协议,最佳的封装方式是Java接口。

作为一个编程框架的RMI,需要为程序员尽可能地隐藏计算细节,如网络错误、程序间通信、分布式系统中的垃圾收集等等。Java RMI框架成功的隐藏网络通信的细节,极大的简化了分布式对象计算程序的开发。当然,本地对象与远程对象毕竟存在重大的差别,因而RMI不可能完全透明。随着分布式系统需求的增长,分布式系统还需要解决其它问题,如事务处理、安全性、一致性管理等等,建立在RMI基础上的其它框架不断被提出。

自定义的远程接口IHello是一个Java接口——封装服务器-客户端之间的协议;IHello基于RMI框架,是java.rmi.Remote的子类型。

在开发环境如NetBeans中创建项目RemoteInterface:
package com.yqj2065;

import java.rmi.Remote;
import java.rmi.RemoteException;
public interface IHello extends Remote{
     /** 
     * 业务方法的简化。简单的返回Hello Remote Method Invocation! 
     * @return 返回Hello Remote Method Invocation!
     * @throws java.rmi.RemoteException 
     */ 
    public String hello() throws RemoteException; 

    /** 
     * 一个简单的业务方法,echo。 
     * @param str  
     * @return echo: str 
     * @throws java.rmi.RemoteException 
     */ 
    public String hello(String str) throws RemoteException; 
}

这里要强调:基础很重要。简单的例子并非无聊或一无是处。

1.java.rmi.Remote

java.rmi.Remote是所有远程接口的父类型。它是一个标记接口,其作用是标识它的(子接口中定义的)方法可以由其他Java虚拟机上的程序调用。

JDK中预定义了一些远程接口,如ActivationInstantiator,ActivationMonitor, ActivationSystem, Activator, DGC, Registry, RMIConnection,RMIServer,也定义了一些实现类,如UnicastRemoteObject。

IHello的每个方法,必须throwsjava.rmi.RemoteException或其父类。除了应用程序本身可能出现的异常之外,每个方法的远程调用都可能失败,例如遭遇服务器关机或服务器超载。从JDK 1.4 开始,已对该异常作出改进,这里暂时不讨论。

2. 生成jar文件

远程接口作为C/S双方之间的“协议”,会被双方程序员所使用。如果需要,应该将下列内容打包:

²       远程接口

²       远程方法抛出的自定义异常

²       中介性质的类,用于在C/S之间传输数据的小类,例如作为方法的参数或返回值的类型。

 

7.1.2 RMI服务器编程

在开发环境如NetBeans中创建项目RMIServer时,需要为它的库添加依赖包。


1. 实现远程服务接口

按照接口与实现相分离原则,客户端并不需要关心远程对象是谁、其实现(方法体)如何,因为客户端针对远程接口编程。而服务器端则需要实现远程接口。

实现类HelloImpl也可以命名为HelloServer,其对象称为远程对象(remote objects)或称为分布式对象(Distributed Object),是指其方法能够跨JVM调用的对象。如果

public classHelloImpl implements IHello

则需要显式导出 [export]对象,简单起见,将HelloImpl作为java.rmi.server.RemoteServer的子类如UnicastRemoteObject的子类。
package rmiserver;
import com.yqj2065.IHello;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class HelloImpl extends UnicastRemoteObject implements IHello { 
    /** 
     * 因为UnicastRemoteObject的构造方法抛出了RemoteException异常,
     * 因此必须定义构造器取代默认构造器,
    public HelloImpl() throws RemoteException {     } 
    @Override public String hello() throws RemoteException { 
        return "Hello Remote Method Invocation!"; 
    } 
    @Override
    public String hello(String str) throws RemoteException { 
        return "echo:" + str ; 
    } 
}

2. 启动服务器

启动服务器,需要一个public static void main(String args[])方法。可以将main放在上面的HelloImpl类中,也可以单独使用一个类,取名RMIServer或RMIServerSetup。

事实上,服务器与客户端之间,还有一个重要问题要解决:客户端怎样找到远程服务

java.rmi.Naming提供RMI 命名服务。
package rmiserver;

import com.yqj2065.IHello;
import java.net.MalformedURLException;
import java.nio.channels.AlreadyBoundException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;

public class RMIServer {
    public static final int port =8000;    
    public static final String registryName = "rmi://localhost:"+port+"/RHello";
    public static void main(String[] args) throws java.rmi.AlreadyBoundException {
        try { 
             
            IHello rhello = new HelloImpl();              
            LocateRegistry.createRegistry(port);  
            Naming.bind(registryName,rhello);  

            System.out.println(">>>>>RMIServer has been started and registered!"); 
        } catch (RemoteException e) { 
            System.out.println("RemoteException");
        } catch (AlreadyBoundException e) { 
            System.out.println("AlreadyBoundException"); 
        } catch (MalformedURLException e) { 
            System.out.println("MalformedURLException"); 
        } 
    }    
}

7.1.3 RMI客户端

RMI的目的是使客户端对远程方法的调用,如同本地对象的方法调用一样,方便自然。

在开发环境如NetBeans中创建项目RMIClient时,为它的库添加依赖包。

注意:服务器和客户端代码中都使用的port和registryName,事实上属于服务器-客户端之间的约定,因而应该放在远程接口中。

请自行重构相关的代码。重构后的RMIClient如下所示。
package rmiclient;

import com.yqj2065.IHello;
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
public class RMIClient {
    //public static final int port =8000;
    //public static final String registryName = "rmi://localhost:"+port+"/RHello";
    public static void main(String[] args) {
         try { 
            IHello h =(IHello) Naming.lookup(IHello.registryName); 
            System.out.println(h.hello()); 
            System.out.println(h.hello("abcd")); 
        } catch (NotBoundException | MalformedURLException | RemoteException e) { 
        } 
    }    
}

7.1.4运行RMI程序

1.启动RMI服务器

在NetBeans中运行RMIServer项目,如图所示。

2.运行客户端程序

客户程序调用h.hello()、hello("abcd")后自动断开与服务器的连接。





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值