【Java】RMI框架搭建
来源:互联网 发布:怎么在mac上装windows 编辑:程序博客网 时间:2024/06/15 07:02
RMI(Remote Method Invoke,即远程方法调用),RMI应用程序包含两个分离的程序,服务端和客户端。典型的服务端程序会创建一些可以访问的远程对象,并等待客户端调用这些对象上的方法。典型的客户端程序获取服务器上一个或多个远程对象的远程引用,然后调用它们上的方法。RMI提供了服务器和客户端通信和来回传递信息的机制。这种应用有时被称为分布式对象应用程序。
下面的插图描述了一个RMI分布式应用程序,它使用RMI注册表来获取对远程对象的引用。服务器调用注册表将名称与远程对象关联(或绑定)。客户端通过服务器注册表中的名称查找远程对象,然后调用它上的方法。另外,RMI系统还可以使用现有Web服务器从服务器到客户机以及从客户端到服务器定义类定义,以便在需要时使用对象。
第一章 RMI程序实现(Oracle官方文档)
一、服务端程序
1.1 Remote 对象
创建一个Compute 接口,继承自Remote。
package compute;import java.rmi.Remote;import java.rmi.RemoteException;public interface Compute extends Remote { <T> T executeTask(Task<T> t) throws RemoteException;}
创建一个Task接口
package compute;public interface Task<T> { T execute();}
1.2 Remote接口实现
package engine;import java.rmi.RemoteException;import java.rmi.registry.LocateRegistry;import java.rmi.registry.Registry;import java.rmi.server.UnicastRemoteObject;import compute.Compute;import compute.Task;public class ComputeEngine implements Compute { public ComputeEngine() { super(); } public <T> T executeTask(Task<T> t) { return t.execute(); } public static void main(String[] args) {//security control if (System.getSecurityManager() == null) { System.setSecurityManager(new SecurityManager()); } try { String name = "Compute"; //Remote Object’s registry name Compute engine = new ComputeEngine();//Making the Remote Object Available to Clients Compute stub = (Compute) UnicastRemoteObject.exportObject(engine, 0); //The code then adds the name to the RMI registry running on the server Registry registry = LocateRegistry.getRegistry(); registry.rebind(name, stub); System.out.println("ComputeEngine bound"); } catch (Exception e) { System.err.println("ComputeEngine exception:"); e.printStackTrace(); } }}
二、客户端程序
package client;import java.rmi.registry.LocateRegistry;import java.rmi.registry.Registry;import java.math.BigDecimal;import compute.Compute;public class ComputePi { public static void main(String args[]) { if (System.getSecurityManager() == null) { System.setSecurityManager(new SecurityManager()); } try { String name = "Compute"; Registry registry = LocateRegistry.getRegistry(args[0]); Compute comp = (Compute) registry.lookup(name); Pi task = new Pi(Integer.parseInt(args[1])); BigDecimal pi = comp.executeTask(task); System.out.println(pi); } catch (Exception e) { System.err.println("ComputePi exception:"); e.printStackTrace(); } } }
package client;import compute.Task;import java.io.Serializable;import java.math.BigDecimal;public class Pi implements Task<BigDecimal>, Serializable { private static final long serialVersionUID = 227L; /** constants used in pi computation */ private static final BigDecimal FOUR = BigDecimal.valueOf(4); /** rounding mode to use during pi computation */ private static final int roundingMode = BigDecimal.ROUND_HALF_EVEN; /** digits of precision after the decimal point */ private final int digits; /** * Construct a task to calculate pi to the specified * precision. */ public Pi(int digits) { this.digits = digits; } /** * Calculate pi. */ public BigDecimal execute() { return computePi(digits); } /** * Compute the value of pi to the specified number of * digits after the decimal point. The value is * computed using Machin's formula: * * pi/4 = 4*arctan(1/5) - arctan(1/239) * * and a power series expansion of arctan(x) to * sufficient precision. */ public static BigDecimal computePi(int digits) { int scale = digits + 5; BigDecimal arctan1_5 = arctan(5, scale); BigDecimal arctan1_239 = arctan(239, scale); BigDecimal pi = arctan1_5.multiply(FOUR).subtract( arctan1_239).multiply(FOUR); return pi.setScale(digits, BigDecimal.ROUND_HALF_UP); } /** * Compute the value, in radians, of the arctangent of * the inverse of the supplied integer to the specified * number of digits after the decimal point. The value * is computed using the power series expansion for the * arc tangent: * * arctan(x) = x - (x^3)/3 + (x^5)/5 - (x^7)/7 + * (x^9)/9 ... */ public static BigDecimal arctan(int inverseX, int scale) { BigDecimal result, numer, term; BigDecimal invX = BigDecimal.valueOf(inverseX); BigDecimal invX2 = BigDecimal.valueOf(inverseX * inverseX); numer = BigDecimal.ONE.divide(invX, scale, roundingMode); result = numer; int i = 1; do { numer = numer.divide(invX2, scale, roundingMode); int denom = 2 * i + 1; term = numer.divide(BigDecimal.valueOf(denom), scale, roundingMode); if ((i % 2) != 0) { result = result.subtract(term); } else { result = result.add(term); } i++; } while (term.compareTo(BigDecimal.ZERO) != 0); return result; }}
三、编译项目
假设我项目的项目路径是D:\workspace\RMIServer,RMIServer是项目名称。
3.1 打jar包
cd D:/workspace/RMIServer/srcjavac compute/Compute.java compute/Task.javajar cvf compute.jar compute/*.class
将打包好的jar包,放到一个固定的目录,如C:/Users/SWL/public_html/classes4。这个jar是服务端和客户端共用的,客户端在向服务端发送请求时,必须要获取这个jar,不然会报ClassNotFound的错误。
3.2 编译服务端
cd D:/workspace/RMIServer/srcjavac -cp C:/Users/SWL/public_html/classes4/compute.jar Engine/ComputeEngine.java
编译时一定要指向事先打包好的jar文件。
3.3 编译客户端
cd D:/workspace/RMIServer/srcjavac -cp C:/Users/SWL/public_html/classes4/compute.jar client/ComputePi.java client/Pi.javamkdir C:/Users/SWL/public_html/classes4/clientcp client\Pi.class C:/Users/SWL/public_html/classes4/client
同样,编译时一定要指向事先打包好的jar文件。
四、运行项目
4.1 安全策略
在运行项目之前,必须先设置安全策略。服务端和客户端在运行之前都是要设置安全策略。
在项目的src目录下创建文件windows.policy,codebase路径指向项目路径。
grant codeBase "file:/D:/workspace/RMIServer/src" { permission java.security.AllPermission;};
4.2 启动rmi的注册监听服务
Jdk的bin目录下有自带的rmiregistry.exe。在启动服务端之前必须先启动rmi的注册服务。rmiregistry服务默认端口为1099,可以自定义端口
cd D:/workspace/RMIServer/srcstart rmiregistry [2001]
4.3 启动服务
java -cpD:/workspace/RMIServer/src;C:/Users/SWL/public_html/classes4/compute.jar-Djava.rmi.server.codebase=file:/C:/Users/SWL/public_html/classes4/compute.jar -Djava.rmi.server.hostname=127.0.0.1 -Djava.security.policy=windows.policy engine.ComputeEngine
启动服务时,一定要指定java项目自身src目录和之前打的jar包地址。另外,也要指定java.rmi.server.codebase、java.rmi.server.hostname、java.security.policy这些系统环境变量。
正常情况下,会有“ComputeEngine bound”的日志打出,此时Server服务正常启动。
如有报错,请仔细检查上述流程。一般报错类型为ClassNotFound或者checkPermisson,前者,检查codebase是否无误,后者检查windows.policy(存放路径、内容)文件是否无误。
4.4 启动客户端
java -cp D:/workspace/RMIServer/src;C:/Users/SWL/public_html/classes4/compute.jar-Djava.rmi.server.codebase=file:/C:/Users/SWL/public_html/classes4/compute.jar -Djava.security.policy=windows.policyclient.ComputePi 127.0.0.1 45
与服务端一样,启动时也要指定启动环境,同时指定系统环境变量。
正常情况下,会有一行输出:
3.141592653589793238462643383279502884197169399
五、总结
在单纯的java项目中,现在是完全没问题的。可以将这份代码及其拷贝随便放置,只要有一个服务端启动,其它的项目中的客户端都可以实现访问。但是,在java web项目想要实现客户端连接服务端,仅仅这些还是不够的。
第二章 Java Web中的实现
在上面的基础下,需要做部分修改。
在原有的ComputePi.java和ComputeEngine.java的main函数中需要加入系统环境变量设置,如下示:
String os = System.getProperty("os.name");if (os.length() > 0 && os.toLowerCase().contains("window")) { System.setProperty("java.rmi.server.codebase","file:/c:/Users/User/public_html/classes4/compute.jar"); System.setProperty("java.security.policy","windows.policy");} else { System.setProperty("java.rmi.server.codebase","file:///home//RMI/RService.jar"); System.setProperty("java.security.policy","linux.policy");}System.setProperty("java.rmi.server.hostname","127.0.0.1");
这部分是设置java.rmi.server.codebase、java.security.policy、java.rmi.server.hostname环境变量。
第三章Linux环境
Linux环境与windows环境的配置方式基本一致,但是有需要特别注意:
1.修改jdk默认的安全策略文件。
/usr/java/jdk1.7.0_71/jre/lib/security/java.policy,在grant中添加:
permission java.security.AllPermission; permission java.util.PropertyPermission "/home/*", "read";permission java.io.FilePermission "/home/*", "read,write";permission java.lang.RuntimePermission "accessClassInPackage.sun.jdbc.odbc";
注意:read、write授权时,指向项目所在的路径。
Windows环境下可能有时也需要添加。
2.在src目录下启动rmiregistry服务。
这个一定要注意,一定要在项目的src目录下启动rmi的注册服务,不然会报错。
参考文档:
- Oracle官方指导文档:https://docs.oracle.com/javase/tutorial/rmi/overview.html
- 安全策略相关:
https://docs.oracle.com/javase/7/docs/technotes/guides/security/PolicyFiles.html
https://docs.oracle.com/javase/7/docs/technotes/guides/security/permissions.html
https://docs.oracle.com/javase/1.5.0/docs/guide/security/index.html
源码+文档
点击这里,下载源码
- 【Java】RMI框架搭建
- spring搭建Java RMI
- Java RMI 服务器框架
- Java RMI 服务器框架
- 初识Java RMI框架
- Java RMI 框架(远程方法调用)
- Java RMI 框架(远程方法调用)
- Java RMI 框架(远程方法调用)
- Java RMI 框架(远程方法调用)
- Java RMI 框架(远程方法调用)
- Java RMI 框架(远程方法调用)
- Java RMI 框架(远程方法调用)
- Java RMI 框架(远程方法调用)
- Java RMI 框架(远程方法调用)
- Java RMI框架(远程方法调用)
- Java RMI 框架(远程方法调用)
- Java RMI 框架(远程方法调用)
- Java RMI 框架(远程方法调用)
- 安卓黑马教学(八)
- 依赖注入简介(一)
- CRC算法 个人学习笔记 直接法、查表法注意点
- AndroidStudio导入jar包和library项目开源库
- Week03_day04 异常
- 【Java】RMI框架搭建
- 浅析JBPM工作流引擎
- 2017 12.29的c语言编程作业
- SDUSTOJ
- [jzoj]3541. 【清华集训2014】破冰派对(组合数+思维)
- 设计模式-工厂方法模式
- 这一定是最简单的MVP+Retrofit
- ffmpeg视音频同步
- 717. 1-bit and 2-bit Characters