设计模式(11)代理模式The Proxy Pattern - 1 - 远程代理rmi_rmi代理转发-程序员宅基地

技术标签: java  设计模式  

代理模式

假设现在需要设置一个监视器Monitor,能够监视糖贩卖机GumballMachine的状态,位置信息、还有多少糖果等。一个简单的想法就是创建一个Monitor的类,构造参数包括GumballMachine。然后就可以调用GumballMachine的一些状态函数。

但是如果GumballMachine在另一台机器上、或者在不同的Java堆上,显然是不能够将GumballMachine的引用传递过来。

这是后就需要使用代理模式来解决。

思路:添加一个新的类叫做MonitorProxy。Monitor和MonitorProxy在一个Java堆上,MonitorProxy这个代理负责与远程的GumballMachine进行沟通,得到数据后把自己伪装成GumballMachine来被MonitorProxy调用。

image-20200424211307458

远程方法

原理

我们要这设计这样一个系统:该系统允许我们调用本地的一个对象,本地的这个对象又能够将请求转发到远程对象上。

四个关键的角色 client 、client helper、service helper、Service

客户端

client helper的作用是让client调用和调用远程remoter效果一样。client helper处理转发了client的请求。

换句话说,从client的角度来看,client就是在调用一个远程的服务。client helper假装自己是service

client helper有和service一样的方法。但是他们的逻辑不同,client helper里面的方法是对service进行响应请求、然后进行参数转换返回给client的。

服务器端

同客户端一样,存在一个Service helper负责处理对话(可能是Socket)

image-20200424212620670

Java RMI(Remote Method Invocation)

Java RMI已经封装好了相关功能。不需要自己动手写网络和I/O相关的代码了。和调用本地的对象一样。唯一的区别时会有网络通讯,可能引起失败抛出异常。

client helper 叫做RMI Stub

service helper 叫做RMI Skeleton

使用RMI

Remote service

一共五步走:

1. 写一个Remote 接口

这个远程接口所定义的方法是client远程调用的。这也就意味着stub和实际的service都要实现这个接口

//1. 扩展java.rmi.Remote接口
public interface MyRemote extends Remote{
    
    //2. 接口中所有的函数都要能够抛出RemoteException异常
    //因为涉及到网络或者I/O,可能出现连接错误等。
    public String sayHello() throws RemoteException;
    //3. 要保证参数和返回值都是基本的数据类型或者是可序列化的。
}

2. 实现Remote接口

远程的class要实现上一步定义的接口

// 1. 注意MyRemoteImpl所继承的类UnicastRemoteObject, 这个类定义了一些远程服务所需要的功能函数。
public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote{
    
    // 2. 抛出这个异常的原因是它的父类可能UnicastRemoteObject会抛出这个异常,所以子类也一样。
    public MyRemoteImpl() throws RemoteException{
    
        
    }
    public String sayHello() {
    
        return "server says, hi";
        // other impl
    }
    
    // 主函数入口。方便起见直接写在了这个类
    public static void main (String[] args) {
    
        
        /*方法一*/
        // 需要运行第四步命令
        try {
    
            	MyRemote service = new MyRemoteImpl();
            	Naming.rebind(“RemoteHello”, service); //在RMI中注册
            } catch(Exception ex) {
    
            	ex.printStackTrace();
            }
        }
    /*方法二*/
    //使用内在注册器,不用单独运行rmiregistry服务。
    try {
    
       //默认端口1099
			Registry registry = LocateRegistry.createRegistry(1099);
			MyRemoteImpl service = new MyRemoteImpl();
			registry.bind("RemoteHello", service); // 在RMI中注册
			System.out.println("Run Successfully");
		} catch (Exception ex) {
    
			ex.printStackTrace();
		}
}

定义好service,需要使用。方法就是把上面的MyRemoteImpl实例化之后,在RMI 中注册这个服务。

try{
MyRemote service = new MyRemoteImpl();
Naming.rebind(“RemoteHello”, service); //起一个名字并且注册。
}catch(Exception ex){

}

3. 生成stubs和skeletons(即service helper和client helper)(java1.5及以后不再被需要)可以略过。

使用rmic工具生成两个类:

rmic MyRemoteImpl
  • MyRemoteImpl_Stub.class 给client
  • MyRemoteImpl_Skel.class 给service
4. 运行RMI registry(在service主机上)
rmiregistry

确保运行的文件能够到达你的类。稳妥的办法是从你的类文件运行此命令。

例如从全限定类名的根目录开始。比如bin/test/MyRemoteImpl.class

那么从bin目录运行

5. 运行远程服务(在service主机上)
java MyRemoteImpl

xxxx是main函数的远程服务。

从全限定类名的根目录开始。同上。

Client

client如何能够得到stub object呢?

import java.rmi.*;
public class MyRemoteClient {
    
    // 主程序入口
    public static void main (String[] args) {
    
    	new MyRemoteClient().go();
    }
    
    
    public void go() {
    
        try {
    
            //查找,返回时Object,需要Cast
			//如果是默认端口和本地地址,可以忽略不写
            MyRemote service = (MyRemote) Naming.lookup(“rmi://127.0.0.1:1099/RemoteHello”);
            String s = service.sayHello();
            System.out.println(s);
        } catch(Exception ex) {
    
        	ex.printStackTrace();
        }
    }
}
  1. client去rmi注册表中查找RemoteHello(是注册时的名字)
  2. Client需要有远程的接口MyRemote.java

client如何获得stub这个类?

  • 方法一:手工交付
  • 方法二:动态下载。放到服务器上动态,如果本地搜索不到就是用http请求下来。

注意事项

  • 在远程服务启动之前,启动rmiregistry。service运行时调用了Naming.rebind(),这个需要rmiregistry服务。所以必须提前运行
  • 忘记使参数或者返回类型序列化。这个编译器检查不出来。只有运行时报错。
  • 忘记把stub给client。

代理模式定义

为另一个对象提供一个代理或者占位符,来控制对他的访问。

几个控制访问的方法:

  • 一个远程代理控制对远程对象的访问

  • 虚拟代理控制对创建成本较高的资源的访问

  • 保护代理控制对于权限资源的访问

远程代理

上面提的那种

虚拟代理

image-20200428173745083

虚拟代理用来标识一个需要昂贵代价去创建的对象。虚拟代理通常来推迟这个对象的创建,直到这个对象被需要时再创建。在被代理对象创建完成之前,虚拟代理扮演者被代理者(RealSubject)的角色, 创建完成之后,虚拟代理把请求转交给被代理对象。

例如:播放在线音乐,当音乐封面没有被下载的时候,虚拟代理充当封面的角色(可以使一串字符串“正在下载封面”,或者其他的某个默认图片), 一旦封面下载完成,虚拟代理就把所有调用的方法委托给实际的图片。

// Head Fist Design Pattern P456
class ImageProxy implements Icon {
    
    ImageIcon imageIcon;
    URL imageURL;
    Thread retrievalThread;
    boolean retrieving = false;
    
    public ImageProxy(URL url) {
     imageURL = url; }
    
    public int getIconWidth() {
    
    	if (imageIcon != null) {
    
    		return imageIcon.getIconWidth();
    	} else {
    
    		return 800;
    	}
    }
    
    public int getIconHeight() {
    
        if (imageIcon != null) {
    
            return imageIcon.getIconHeight();
        } else {
    
            return 600;
        }
    }
    
    public void paintIcon(final Component c, Graphics g, int x, int y) {
    
        if (imageIcon != null) {
    
        	imageIcon.paintIcon(c, g, x, y);
        } else {
    
        	g.drawString(“Loading CD cover, please wait..., x+300, y+190);
        if (!retrieving) {
    
        	retrieving = true;
       		retrievalThread = new Thread(new Runnable() {
    
                public void run() {
    
                    try {
    
                    	imageIcon = new ImageIcon(imageURL, “CD Cover”);
                    	c.repaint();
                    } catch (Exception e) {
    
                    	e.printStackTrace();
                    }
                 }
            });
        	retrievalThread.start();
        	}
        }
    }
}
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/KuXiaoQuShiHuai/article/details/107115182

智能推荐

shell函数的使用方法,轻松写脚本_shell脚本函数调用怎么写-程序员宅基地

文章浏览阅读2k次。目录shell函数1.函数的作用2.函数的基本格式3.函数注意事项4.函数调用的方法5.函数的返回值6.函数的传参7.在外部调用函数8.函数变量的作用范围9.函数的递归shell函数1.函数的作用语句块定义成函数约等于别名,定义函数,再引用函数封装的可重复利用的具有特定功能的代码2.函数的基本格式法一:[function] 函数名 (){ 命令序列 [return x] #使用return或者exit可以显式的结束函数} 法二:函数名(){ 命令序列}3._shell脚本函数调用怎么写

设计模式之七适配器模式_软件设计模式实验7适配器模式-程序员宅基地

文章浏览阅读157次。概念适配器模式(Adapater Pattern)是指将一个类的接口转换成用户期望的另一个接口,使原本接口不兼容的类可以一起工作,属于结构型设计模式。场景场景一已经存在的类的方法和需求不匹配(方法结果相同或者相似)的情况场景二适配器模式不是软件设计初始阶段考虑的设计模式,是随着软件的发展,由于不同的产品、不同的厂家造成功能类似而接口不同的问题的解决方法,有点亡羊补牢的感觉。生活中电源..._软件设计模式实验7适配器模式

首届《Mr媛杯》程序媛选帅大赛开幕-程序员宅基地

文章浏览阅读180次。点击欧盟IT那些事关注我们公告:因企鹅审核规定,本公众号从《德国IT那些事》更名为《欧盟IT那些事》。你们要的比赛来了。宣传防骗,人人有责,让骗子无人可骗。近期在德国各地华人群中,频繁..._选帅哥大赛开始

WebSocket的那些事(1-概念篇)_qwebsocket的唯一标识-程序员宅基地

文章浏览阅读1.9k次,点赞5次,收藏6次。根据RFC 6455标准,Websocket协议提供了一种标准化的方式在客户端和服务端之间通过TCP连接建立全双工、双向通信渠道。它是一种不同于HTTP的TCP协议,但是被设计为在HTTP基础上运行。Websocket交互始于HTTP请求,该请求会通过HTTPUpgrade请求头去升级请求,进而切换到Websocket协议。我们可以看到在该请求报文中有两个特殊的请求头,一个是Upgrade请求头,代表升级为websocket协议。还有一个是Connection请求头,代表升级连接。_qwebsocket的唯一标识

hive 查询写入到hdfs_经过hive计算后,写入到hdfs中的统计数据,想要展现给用户,最佳方式是什么-程序员宅基地

文章浏览阅读3.5k次。将hive数据 查询出结果保存在hdfs set mapred.reduce.tasks = 1;set mapred.job.name=recommend_$idate;insert overwrite directory '/user/client/' ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t'select * from t..._经过hive计算后,写入到hdfs中的统计数据,想要展现给用户,最佳方式是什么

wechatpay-apache-httpclient案列demo_wechatpay-apache-httpclient 连接池-程序员宅基地

文章浏览阅读2.5k次。gitHop地址https://github.com/wechatpay-apiv3/wechatpay-apache-httpclientimport cn.hutool.json.JSONObject;import com.fasterxml.jackson.databind.ObjectMapper;import com.fasterxml.jackson.databind.node.ObjectNode;import com.vworld365.common.exception.Req_wechatpay-apache-httpclient 连接池

随便推点

(c语言)Saving James Bond - Hard Version (30分)_7-11 saving james bond - hard version (30 分)测试点3,4-程序员宅基地

文章浏览阅读624次,点赞7次,收藏10次。原题题目(谷歌翻译)全检查点通过图闲谈说真的 第一次没有上CSDN先瞄一眼人家的思路 然后再上手做这道题自己完全独立做 竟然能够完全有思路尤其在对于Dijkstra算法这道题其实也就是变了一下形就是把每条边的权值当成了1然后再分别计算每个点的最短路径只是还要考虑另外几个点下面思路分析的时候 会专门讲的总而言之 真的当这道题做出来的时候我觉得喜悦的感觉已经少了很多了更多的感觉是理所应当这就是我应该做出来的题的感觉现在也许比较刚开始处理一道题 根本不知所云一道题可能_7-11 saving james bond - hard version (30 分)测试点3,4

企业云原生之微服务全面解析_云服务 微服务-程序员宅基地

文章浏览阅读406次。企业云原生之微服务全面解析_云服务 微服务

PHP的构成及生命周期_php返回数据结构中生命周期时间-程序员宅基地

文章浏览阅读1.2w次,点赞3次,收藏19次。一、PHP开源源代码下载地址:https://github.com/php/php-src.git二、PHP的构成1、目录结构2、目录分析(1)sapi目录是PHP的应用接口层。(2)main为php的主要代码,主要是输入/输出、Web通信、PHP框架的初始化操作等,比如fastcgi协议的解析、扩展的加载、PHP配置的解析等工作都是在这里完成的。(3)Zen..._php返回数据结构中生命周期时间

flask实现token登录验证及跨域问题options请求问题_flask options-程序员宅基地

文章浏览阅读1.8k次。本文主要展示如何用flask实现token验证及跨域问题,本文主要参考文章为:Flask实现token认证 - 简书但基于此,更详细的实现了包括解决频繁options问题、全局请求token验证问题。run.pyfrom flask import Flask, g, jsonify, redirect, request, make_responsefrom flask_httpauth import HTTPBasicAuthfrom itsdangerous import Ti.._flask options

漏洞复现-CVE-2023-33246 Apache RocketMQ RCE漏洞原理与复现-程序员宅基地

文章浏览阅读2.8k次。服务端管理员调用的接口没有鉴权,且将内容直接拼接到执行的命令,没有做过滤。_cve-2023-33246

如何使用ASP隐藏图片的地址和判断是否盗链_asp 隐藏 原图片地址-程序员宅基地

文章浏览阅读1.1k次。第一种方法 使用response.Redirect : Dim filename filename=request("file") response.Redirect "/images/" & filename & ".jpg" %> 第二种方法 使用server.createObject("ADODB.Stream"): Re_asp 隐藏 原图片地址