Java之RMI


前言

RMI——Remote Method Invocation,它是Java提供的一组开发分布式应用程序的API,其运用的设计模式的思想是代理模式。

RMI由Java提供,这跟RPC的区别就很明显了,RPC(Remote Procedure Call)远程过程调用是可以由不同的语言实现,RMI是面向对象的,某种意义上来说(目的一样,都是方便程序员进行分布式开发),在Java语言中,RMI是RPC下的一种具体体现。

在维基百科中有这么一段解释 :

The Java Remote Method Invocation (Java RMI) is a Java API that performs remote method invocation, the object-oriented equivalent of remote procedure calls (RPC), with support for direct transfer of serialized Java classes and distributed garbage collection.

根据上述定义,RPC其实更像一种协议,在这个协议下有着各个版本的具体实现,常见的 RPC 框架有阿里的 Dubbo,微博开源的 Motan,Google 开源的 gRpc,百度开源的 brpc,蚂蚁金服开源的 sofa-rpc 等等。


RMI的通信过程

在这里插入图片描述
在维基百科中,有如下的解释,意思是指在java 1.2之前,与Stub对象对话的是Skeleton对象,在Stub对象将调用传递给Skeleton的过程中,其实这个过程是通过JRMP协议实现转化的,通过这个协议将调用从一个虚拟机转到另一个虚拟机。在Java 1.2之后,与Stub对象直接对话的是Server程序,而Skeleton对象被移除了。*

A typical implementation model of Java-RMI using stub and skeleton objects. Java 2 SDK, Standard Edition, v1.2 removed the need for a skeleton.

关于Stub(存根)对象是存在于本地的,它客户端也不是直接和Server交互,而是通过这个Stub进行交互的,而这个地方也是代理模式的体现,一方面出于安全的考虑一方面方便模块的解耦。Stub实现了远程对象向外暴露的接口,它的方法和远程对象暴露的方法的签名(方法名,方法的参数类型)是相同的。


RMI的实现(运用了代理模式和外观模式的设计思想)

关于RMI的实现,Java提供了几种方式,这里是通过实现Remote接口实现的。程序的背景模拟一个买卖系统的流程,在实践过程中所遇到的问题也一并记录下来:

在facade包下有账号服务,货品服务,账单服务和支付服务,都很简单,只是在控制台打印简单的信息而已,这一部分大家可以选择性的忽略:

package facade;

//账号服务
public class AccountService {
    private static int isLogin = 0;

    public boolean login(){
        isLogin = 1;
        System.out.println("登录成功");
        return true;
    }
    public boolean valid(){
        if(isLogin == 0){
            System.out.println("您未登录");
            return false;
        }
        System.out.println("您已登录");
        return true;
    }
    public void serviece(){
        System.out.println("账户服务被调用");
    }
}
//货品服务
public class GoodsService {

    public int getHouse() {
        return house;
    }

    public void setHouse(int house) {
        this.house = house;
    }

    public int getCar() {
        return car;
    }

    public void setCar(int car) {
        this.car = car;
    }

    private int house = 0;
    private int car = 0;

    public void service(){
        System.out.println("货物服务被调用");
    }
}
//订单服务
public class OrderService {

    private int housePrice = 5000000;
    private int carPrice = 400000;

    public void service(){
        System.out.println("订单服务被调用");
    }
    public int order(int countHouse, int countCar){
        if(countHouse != 0)
            System.out.println("您购买了"+countHouse+"套房子,单价为"+housePrice);
        if(carPrice != 0)
            System.out.println("您购买了"+carPrice+"辆车,单价为"+housePrice);
        int momey = countHouse * housePrice + carPrice * countCar;
        System.out.println("总价格为:"+momey+"元");
        return momey;
    }
}
//支付服务
public class PayService {
    public void service(){
        System.out.println("支付服务被调用");
    }
    public boolean pay(int money, int countMoney){
        if(money < countMoney){
            System.out.println("您支付的金额小于总价请继续支付");
            return false;
        }
        if(money > countMoney){
            int price = money - countMoney;
            System.out.println("您支付的金额大于总金额,将退还"+price+"元");
        }
        return true;
    }
}

基础服务就到这里了,接下来是一个外观接口,为了支持RMI,该接口需要继承Remote接口:

package facade;

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

public interface CustomerInterface extends Remote {
    void shoppingService() throws RemoteException;
    void setMoney(int money) throws RemoteException;
    void setCoutHourse(int coutHourse) throws RemoteException;
    void setCountCar(int countCar) throws RemoteException;
    void login() throws RemoteException;
    void service() throws RemoteException;
}

该接口的实现:在这个代码中要注意LocateRegistry.createRegistry(port);这个方法,该方法是创建本地注册表,如果你没有这段代码,你需要手动启动,在Java的bin文件夹下的rmiregistry.exe,否则,会抛出拒接连接的异常。

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

public class CustomerFacade extends UnicastRemoteObject implements CustomerInterface  {
    private AccountService accountService;
    private GoodsService goodsService;
    private OrderService orderService;
    private PayService payService;

    public void setMoney(int money) {
        this.money = money;
    }

    private int money;
    private int countPrice;

    public void setCoutHourse(int coutHourse) {
        this.coutHourse = coutHourse;
    }
    public void setCountCar(int countCar) {
        this.countCar = countCar;
    }

    private int coutHourse;
    private int countCar;

    public CustomerFacade() throws RemoteException {
        super();
        intit();
    }

    private void intit() {
        accountService = new AccountService();
        goodsService = new GoodsService();
        orderService = new OrderService();
        payService = new PayService();
    }
    public void service(){
        System.out.println("开始购物");
    }
    @Override
        public void shoppingService() throws RemoteException {
            boolean result = isLogin();
            if(result){
                toGoodsService();
                toOrderService();
                toPayService();
            }else{
                System.out.println("");
            }
        }

    private void toPayService() {
        payService.service();
        payService.pay(money, countPrice);
    }

    private void toOrderService() {
        orderService.service();
        countPrice = orderService.order(coutHourse,countCar);
    }

    public void login() {
        accountService.login();
    }

    private void toGoodsService() {
        goodsService.service();
        if(countCar != 0)
            goodsService.setCar(countCar);
        if(coutHourse != 0)
            goodsService.setCar(coutHourse);
    }

    private boolean isLogin() {
        return accountService.valid();
    }
    public static void main(String[] args) throws Exception {
        String port = "1099";
        String host = "localhost";

        if (System.getSecurityManager() == null) {
            System.setSecurityManager(new SecurityManager());
        }
        CustomerFacade facade = new CustomerFacade();
        /**
         * 创建一个Registry对象.
         * LocateRegistry用于获取名字服务或创建名字服务.
         * 调用LocateRegistry.createRegistry(int port)方法可以在某一特定端口创建名字服务,
         * 从而用户无需再手工启动rmiregistry
         */
        LocateRegistry.createRegistry(1099);
        Naming.bind("//" + host + ":" + port + "/CustomerFacade",
                facade);

        System.out.println("Service Bound...");

    }
}

另外,由于Java的安全策略原因,也会造成程序运行出现异常,此时要到Java文件下的jre\lib\security目录找到java.policy文件,在最后添加如下语句,表示允许连接的端口

permission java.net.SocketPermission "*:1024-65535",
          "connect,accept,resolve";
 permission java.net.SocketPermission "*:1-1023",
          "connect,resolve";

接下来是关于客户端的程序,这里要注意一下,由前言我们可以知道,客户端是通过存根跟服务器打交道的,所以,客户端需要一个类来模拟服务端,让客户端认为它是直接跟服务端打交道的,在我们这个程序中,承担这个任务的就是CustomerInterface接口。

该接口提供的方法和服务端的CustomerInterface接口一毛一样,直接复制粘贴过来就行。这里要注意一下!!!!改程序的全限定名要一致!否则客户端运行会出现类型无法转换的问题,也就是从包名到类名要跟服务端的程序一致。可以将客户端程序放在另一个项目中~

import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import facade.CustomerInterface;

public class CustomerClient {
    public static void main(String[] args) {
        String port = "1099";
        String host = "localhost";
        CustomerInterface facade;
        try {
        //CustomerFacade是服务端绑定的一个标志,你也可以修改成别的
        //通过Naming.lookup返回一个引用,而facade对象在得到引用后就是我们的Sub,通过facade我们可以访问远程的服务了
            facade = (CustomerInterface) Naming.lookup ("rmi://" +host + ":" + port + "/CustomerFacade");
            facade.shoppingService();
            facade.login();
            facade.service();
            facade.setCountCar(5);
            facade.setCoutHourse(2);
            facade.shoppingService();
            facade.setCoutHourse(12000000);
            facade.shoppingService();
        } catch (NotBoundException e) {
            e.printStackTrace();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
}

——————————————

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

legendaryhaha

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值