网站建设 图纸网,中国外包公司,网页封装app,网站卖给别人后做违法信息【0】README
1#xff09;本文文字转自 core java volume 2#xff0c; 旨在学习 java 分布式对象的相关知识#xff1b; 2#xff09; RMI 的实例程序为原创#xff1b; 3#xff09; RMI部署步骤的测试用例#xff0c;参见 http://blog.csdn.net/pacosonswjtu/arti…【0】README
1本文文字转自 core java volume 2 旨在学习 java 分布式对象的相关知识 2 RMI 的实例程序为原创 3 RMI部署步骤的测试用例参见 http://blog.csdn.net/pacosonswjtu/article/details/50705258 【1】知识背景
1每过一段时间 程序员社区就开始考虑“无所不在的对象”作为所有问题的解决之道 2 当一台计算机上的某个对象需要调用另一台计算机上的某个对象时 他就会发送一个包含这个请求的详细信息的网络消息 3一旦该远程对象得到了客户端请求的东西 就将他发送给 客户端 4在本章我们将聚焦在Java的分布式编程技术上特别是用于两个Java虚拟机可以运行在不同的计算机上之间通信的远程方法调用RMI协议。 5RMI Remote Method Invocation 远程方法调用协议 【2】客户与服务器的角色
1所有分布式编程技术的基本思想都很简单客户计算机产生一个请求然后将这个请求经由网络发送到服务器。服务器处理这个请求并发送回一个针对该客户端的响应供客户端进行分析。图11-1展示了这个过程。
2problemsolution
2.1problem我们真正想要的是这样一种机制客户端程序员以常规的方式进行方法调用而无需操心数据在网络上传输或者解析响应之类的问题。2.2solution解决的办法是在客户端为远程对象安装一个代理proxy。干货——引入代理的概念
3代理 是位于客户端虚拟机中的一个对象它对于客户端程序来说看起来就像是要访问的远程对象一样。客户调用此代理进行常规的方法调用。而客户端代理负责与服务器进行联系。 4problemsolution
4.1problem实现服务的程序员也不希望因与客户端之间的通信而被绊住。4.2solution解决方法是在服务器端安装第二个代理对象。该服务器代理与客户端代理进行通信并且它将以常规方式调用服务器对象上的方法参见图11-2。
5代理之间是如何通信的呢这要看以什么技术来实现它们。通常有三种选择options 干货——代理间的通信技术
o1 CORBA通用对象请求代理架构 支持任何编程语言编写的对象之间的方法调用。CORBA使用Internet Inter-ORB协议IIOP支持对象间的通信。o2 Web服务架构是一个协议集有时统一描述为WS-*。它也独立于编程语言的不过它使用基于XML的通信格式。用于传输对象的格式则是简单对象访问协议SOAP。o3 RMIJava的远程方法调用技术 支持Java的分布式对象之间的方法调用。 干货——引入RMI
5.1与RMI不同CORBA与SOAP都是完全独立于语言的。客户端与服务器程序可以由C、C、Java或者其他语言编写。 5.2 Sun 开发了一个更简单的机制 称为远程方法调用-RMI 专门针对java 应用之间的通信 【3】远程方法调用RMI
1 分布式计算的关键是远程方法调用。 在一台机器称为客户端上的某些代码希望调用在另一台机器远程对象上的某个对象的一个方法。要实现这一点方法的参数必须以某种方式传递到另一台机器上而服务器必须得到通知去定位远程对象并执行要调用的方法并且必须将返回值传递回去。干货——分布式计算的关键是远程方法调用 【3.1】存根与参数编组
1存根 当客户代码要在远程对象上调用一个远程方法时实际上调用的是代理对象上的一个普通的方法我们称此代理对象为存根stub。 干货—— 存根是client 上的 agent object
1.1看个荔枝 Warehouse centralWarehouse get stub object; double price centralWarehouse.getPrice(“Blackwell Toaster”);1.2存根位于客户端机器上而非服务器上。它知道如何通过网络与服务器联系。1.3存根将远程方法所需的参数打包成一组字节。1.4参数编组对参数编码的过程称作参数编组parameter marshalling参数编组的目的是将参数转换成适合在虚拟机之间进行传递的格式。在RMI协议中对象是使用序列化机制进行编码的第1章描述了这种机制。在SOAP协议中对象被编码为XML。 干货——参数编组定义
2总的来说客户端的存根方法构造了一个信息块它由以下几部分组成parts
p1被使用的远程对象的标识符p2被调用的方法的描述p3编组后的参数
3然后存根将此信息发送给服务器。在服务器一端接收对象执行以下动作actions
a1定位要调用的远程对象a2调用所需的方法并传递客户端提供的参数a3捕获返回值或该调用产生的异常a4将返回值编组打包送回给客户端存根。
4 反编组客户端存根对来自服务器端的返回值或异常进行反编组就成为了调用存根的返回值。如果远程方法抛出了一个异常那么存根就在客户端发起调用的处理空间中重新抛出该异常。图11-3展示了一次远程方法调用的信息流。
Attention 这个过程显然很复杂不过好消息是这一切都是完全自动的而且在很大程度上它对程序员是透明性。 【4】RMI 编程模型
【4.1 接口实现】
1 如何实现和启动服务器与客户端程序。 干货——如何实现和启动服务器与客户端程序
step1远程对象的能力是由在客户端和服务器之间共享的接口所表示的。例如以下程序的接口描述了远程仓库对象所提供的服务
public interface Warehouse extends Remote
{double getPrice(String description) throws RemoteException;
}
对以上代码的分析Analysis
A1远程对象的接口必须扩展Remote接口它位于java.rmi包中。A2接口中的所有方法还必须声明抛出RemoteException异常这是因为远程方法调用与生俱来就缺乏本地调用的可靠性远程调用总是存在失败的可能。
step2 接下来在服务器端必须提供这样的类它真正实现了在远程接口中声明的工作。
public class WarehouseImpl extends UnicastRemoteObject implements Warehouse
{public WarehouseImpl() throws RemoteException{prices new HashMapString, Double();prices.put(Blackwell Toaster, 24.95);prices.put(ZapXpress Microwave Oven, 49.95);}public double getPrice(String description) throws RemoteException{Double price prices.get(description);return price null ? 0 : price;}private MapString, Double prices;
}
Attention UnicastRemoteObject类 你可以看出这个类是远程方法调用的目标因为它继承自UnicastRemoteObject这个类的构造器使得它的对象可供远程访问。 2problemsolution
2.1problem有时候可能不希望服务器类继承UnicastRemoteObject也许是因为实现类已经继承了其他的类。2.2solution在这种情况下读者需要亲自初始化远程对象并将它们传给静态的exportObject方法。如果不继承UnicastRemoteObject可以在远程对象的构造器中像下面这样调用exportObject方法。 UnicastRemoteObject.exportObject(this, 0); // 第二个参数是0表明任意合适的端口都可用来监听客户端连接。
Attention Unicast这个术语是指我们是通过产生对单一的IP地址和端口的调用来定位远程对象的这一事实。这是Java SE中唯一支持的机制。更复杂的分布式对象系统诸如JINI会考虑到对在多个不同的服务器上的远程对象的”Multicast”查找。 干货——这里提到了JINI intro to jini ( from http://baike.baidu.com/link?url2uT-UgPQerl5Xze13Xqyg0e9eVTH5SOI6ucWnFw_nMKHw8754Lc1H_eM94gxiM7U0U9FUttdQd1yZCiBeYvVlK) Jini(Java Intelligent Network Infrastructure)是Sun公司的研究与开发项目它能极大扩展Java技术的能力。Jini技术可使范围广泛的多种硬件和软件—即可与网络相连的任何实体—能够自主联网。 Jini可以使人们极其简单地使用网络设备和网络服务就象今天我们使用电话一样—通过网络拨号即插即用。Jini的目标是最大限度地简化与网络的交互性。 【4.2】RMI注册表
1problemsolution
1.1problem要访问服务器上的一个远程对象时客户端首先需要一个本地的存根对象。可是客户端如何对该存根发出请求呢最普通的方法是调用另一个服务对象上的一个远程方法以返回值的方式取得存根对象。然而这就成了先有鸡还是先有蛋的问题。1.2solution第一个远程对象总要通过某种方式进行定位。为此JDK提供了自举注册服务bootstrap registry service。
2如何注册一个远程对象 干货——如何注册一个远程对象 服务器程序使用自举注册服务来注册至少一个远程对象。要注册一个远程对象需要一个RMI URL和一个对实现对象的引用。RMI的URL以rmi:开头后接服务器以及一个可选的端口号接着是远程对象的名字。例如 rmi://localhost:99/central_warehouse 3默认情况下主机名是localhost端口为1099。 服务器告诉注册表在给定位置将该对象关联或”绑定”到这个名字。
3.1下面的代码将一个WarehouseImpl对象注册到了同一个服务器上的RMI注册表中 干货——绑定远程对象到server上的RMI注册表中 WarehouseImpl centralWarehouse new WarehouseImpl(); Context namingContext new InitialContext(); namingContext.bind(“rmi:central_warehouse”, centralWarehouse);3.2 看个荔枝下面的程序只是构造并注册了一个WarehouseImpl对象
Attention 基于安全原因一个应用只有当它与注册表运行在同一个服务器时该应用才可以绑定、取消绑定或重绑定注册对象的引用。 4客户端可以通过下面的调用枚举所有注册过的RMI对象 干货——注意是client 端枚举 出 server 端 注册过的RMI对象
EnumerationNameClassPair e namingContext.list(rmi://regserver.mycompany.com)
5NameClassPair是一个助手类 它包含绑定对象的名字和该对象所属类的名字。
5.1看个荔枝 例如下面的代码可以显示所有注册对象的名字 while (e.hasMoreElements()) System.out.println(e.nextElement().getName());
6客户端可以通过下面的方式来指定服务器和远程对象的名字以此获得访问远程对象所需的存根 干货——client端获得存根
String url rmi://regserver.mycompany.com/central_warehouse;
Warehouse centralWarehouse (Warehouse) namingContext.lookup(url);
Attention 在一个全局注册表中想保持一个名字的惟一性非常困难因此不应该将此技术作为定位服务器端对象的一般方法。相反自举服务只应该用来注册非常少的远程对象。然后使用这些对象来定位其他的对象。 干货——自举服务的局限性 7 代码11-4展示了客户端如何获得远程仓库对象的存根并调用远程的getPrice方法。 【4.3】部署程序(you can also refer to http://www.cnblogs.com/leslies2/archive/2011/05/20/2051844.html)
0部署前的准备
0.1创建两个目录 分别存放用于启动server 和 client 的类 server/ WarehouseServer.class Warehouse.class WarehouseImpl.class client/ WarehouseClient.class Warehouse.class 0.2当部署RMI 应用时 通常需要动态地将类交付给运行程序其中一个例子就是RMI注册表。请记住注册表的一个实例要服务许多不同的RMI应用。RMI注册表需要有权限访问注册的服务接口的类文件但是当注册表启动时无法预测将来会产生的所有注册请求。因此RMI 注册表会动态地加载之前从未遇到过的所有远程接口的类文件0.3动态交付的类文件是通过标准的web server发布的。在我们所举的case中 server程序 需要使用 Warehouse.class 文件 对于 RMI 注册表来说是可以获得的因此将这个文件放到了第三个称为download 目录中的 download/ Warehouse.class 0.4下面我们用 web server 来访问这个目录中的内容NanoHTTPD web server
1当应用被部署时服务器、RMI注册表、Web服务器和客户端可以定位到四台不同的计算机上参见图11-5。但是出于测试的目的我们将只使用一台计算机。
Attention 由于安全的原因作为JDK一部分的rmiregistry服务只允许来自同一台主机的绑定调用。也就是说服务器和rmiregistry进程需要定位在同一台计算机上。但是RMI架构允许更通用的支持多台服务器的RMI注册表实现。 干货——基于安全原因服务器和rmiregistry进程需要定位在同一台计算机上 2测试远程方法调用
step1打开一个新的控制台窗口转到download目录然后将NanoHTTPD.javaNanoHTTPD web 服务器复制到这个目录中。使用下面的命令来编译该源文件并启动这个web服务器 java NanoHTTPD 8080 step2打开另一个控制台窗口转到不包含任何类文件的某个目录并启动RMI注册表rmiregistry
Attention
A1在启动RMI注册表之前请确保CLASSPATH环境变量没有进行任何设置并仔细检查当前目录是否确实不包含任何类文件。否则RMI注册表可能会找到一些假冒的类文件这使得注册表需要从另一个不同的来源下载额外的类文件时产生混淆。A2简单地讲每个存根对象都有一个代码基项指出了它是从何处加载的。这个代码基被用来加载它所依赖的类。如果RMI注册表在本地找到了这样的类那么它的代码基就会被设置为错误的值。A3推荐在server 程序中通过代码注册通讯端口和注册通讯路径 干货——不推荐使用 rmiregistry命令行 // 注册通讯端口 LocateRegistry.createRegistry(1099); // 注册通讯路径 Naming.rebind(“rmi://localhost:1099/warehouseService”, warehouseService); step3现在已经准备好启动服务器了。打开第三个控制台窗口转到server目录并执行下面的命令 java -Djava.rmi.server.codebasehttp://localhost:8080/ WarehouseServer // java.rmi.server.codebase属性指出了服务类文件的URL。服务器程序将这个URL传递给RMI注册表。 Warning 确保代码基URL以斜杠”/”结尾非常重要。step4最后打开第四个控制台窗口转到client目录运行 你将会看到一条短消息表示运行方法被成功调用 参见图11-6所示 【4.4】记录 RMI 活动的日志
1 如果用下面的选项启动服务器 -Djava.rmi.server.logCallstrue WarehouseServer // 那么服务器会在其控制台上记录所有的远程方法调用到日志中。 java -Djava.rmi.server.logCallstrue -Djava.rmi.server.codebasehttp://localhost:8080/ com.corejava.chapter11.server.WarehouseServer 2 如果想查看额外的日志信息就必须用标准的Java日志API配置日志记录器。
2.1 可以用下面的内容创建一个logging.properties文件。 handlersjava.util.logging.ConsoleHandler sun.rmi.loader.levelFINE java.util.logging.ConsoleHandler.levelFINE java.util.logging.ConsoleHandler.formatterjava.util.logging.SimpleFormatter 2.2我们可以对设置进行调整方法是为每一个记录器设置单独的等级而不是设置全局等级。例如要跟踪类的加载行为可以设置为 sun.rmi.loader.levelFINE 2.3用以下选项启动RMI注册表 -Djava.util.logging.config.filedirectory/logging.properties 2.4用以下选项启动客户端与服务器 -Djava.util.logging.config.filedirectory/logging.properties 2.5表11-1 列出了所有的RMI 日志记录器