已经不少文章指导如何在springboot里对RMI的使用
https://developer.aliyun.com/article/624251
上文只是让RMI client可以调用RMI Server的一个简单方法,参数和结果都是JAVA的基本类型。
如果RMI Server端封装的接口涉及模板类,接口内部调用模板类(具体实现在RMI客户端)的方法,这个情况就复杂一些了。
比如,基本公用接口如下: public interface ICompute { <T> T executeTask(ITask<T> t) throws RemoteException; }
public interface ITask<T> { T execute(); }
RMI服务端实现如下
@Service public class ComputeEngine implements ICompute { public ComputeEngine() { super(); } public <T> T executeTask(ITask<T> t) { return t.execute(); } }
RMI client端实现了一个按位数计算Π的任务类:
public class Pi implements ITask<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; } }
按普通方式启动RMI server/client,
Pi pi = new Pi(45); System.out.println("got Pi task result ================>" + computeService.executeTask(pi)); 上面的调用会报错的:
java.lang.ClassNotFoundException: com.pinnet.task.Pi (no security manager: RMI class loader disabled)
解决方案如下:
原因是: RMI server端无法序列化Pi这个类,server端的需要有这个类的JAR包或可搜索到
[1]RMI Server/Client都需要尽早设置一个SecurityManager,
@Bean
public SecurityManager dummyStarter() {
SecurityManager sm = System.getSecurityManager();
if (sm == null) {
sm = new SecurityManager();
System.setSecurityManager(sm);
}
return sm;
}
[2]生成client.policy/server.policy 安全策略文件
grant {
permission java.security.AllPermission;
};
[3]客户端定制/继承的子类必须按目录结构部署到codebase指定的URL里
http://192.168.0.214:9080/classes/
[4]按如下参数启动client/server
java18 -Djava.rmi.server.codebase=http://192.168.0.214:9080/classes/ -Djava.security.policy=server.policy -jar do-server-1.0-SNAPSHOT.jar
java18 -Djava.rmi.server.codebase=http://192.168.0.214:9080/classes/ -Djava.security.policy=client.policy -jar do-client-1.0-SNAPSHOT.jar
调用即可顺利完成.
这样就是实现了把高强度的计算任务从RMI client递送给RMI Server去执行,结果返回给client就好了.