两台机器:
1.Linux(192.168.80.129)
2.Windows(192.168.80.1)
Linux作为服务注册机,Windows作为服务提供者。
服务接口:
package com.zzj.jndi.service;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface JndiService extends Remote {
public int getNext() throws RemoteException;
}
接口实现类:
package com.zzj.jndi.service.impl;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import com.zzj.jndi.service.JndiService;
/**
* Unicast:单播(点到点)
* @author lenovo
*
*/
public class JndiServiceImpl extends UnicastRemoteObject implements JndiService {
private static final long serialVersionUID = 1L;
private int next;
public JndiServiceImpl() throws RemoteException {
super();
}
/**
* 非线程安全的
*/
public int getNext() throws RemoteException {
return ++next;
}
}
服务注册类:
package com.zzj.jndi.registry;
import java.rmi.registry.LocateRegistry;
public class RMIRegistry {
public static void main(String[] args) throws Exception {
/* 必须将java.rmi.server.hostname设置为服务器对外公布的IP,
* 否则客户端会去找127.0.0.1
*/
System.setProperty("java.rmi.server.hostname", "192.168.80.129");
System.setProperty("java.security.policy", "/root/Desktop/rmi.policy");
if (System.getSecurityManager() == null) {
System.setSecurityManager(new SecurityManager());
}
//注册RMI服务器端口
LocateRegistry.createRegistry(8080);
System.out.println("注册服务已启动...");
System.in.read(); // press any key to exit
System.out.println("exit!");
}
}
服务提供者:
package com.zzj.jndi.provider;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import com.zzj.jndi.service.JndiService;
import com.zzj.jndi.service.impl.JndiServiceImpl;
public class RmiServiceProvider3 {
public static void main(String[] args) throws Exception {
System.setProperty("java.security.policy", "E:\\eclipsejee\\javase\\src\\rmi.policy");
if(System.getSecurityManager() == null){
System.setSecurityManager(new SecurityManager());
}
Registry registry = LocateRegistry.getRegistry("192.168.80.129", 8080);
JndiService jndiService = new JndiServiceImpl();
registry.bind("jndiService", jndiService);
System.out.println("Jndi服务已绑定...");
}
}
安全策略文件rmi.policy:
grant {
permission java.security.AllPermission;
};
将服务注册类RMIRegistry和服务接口类JndiService打成Jar包RMIRegistry.jar,放到Linux上,启动服务注册类:
java -cp RMIRegistry.jar com.zzj.jndi.registry.RMIRegistry
在Windows上启动提供者,抛出以下错误:
Exception in thread "main" java.rmi.ServerException: RemoteException occurred in server thread; nested exception is:
java.rmi.AccessException: Registry.Registry.bind disallowed; origin /192.168.80.1 is non-local host
at sun.rmi.server.UnicastServerRef.oldDispatch(UnicastServerRef.java:459)
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:293)
at sun.rmi.transport.Transport$2.run(Transport.java:202)
at sun.rmi.transport.Transport$2.run(Transport.java:199)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Transport.java:198)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:567)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:828)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.access$400(TCPTransport.java:619)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$1.run(TCPTransport.java:684)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$1.run(TCPTransport.java:681)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:681)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(StreamRemoteCall.java:275)
at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:252)
at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:378)
at sun.rmi.registry.RegistryImpl_Stub.bind(Unknown Source)
at com.zzj.jndi.provider.RmiServiceProvider3.main(RmiServiceProvider3.java:19)
Caused by: java.rmi.AccessException: Registry.Registry.bind disallowed; origin /192.168.80.1 is non-local host
at sun.rmi.registry.RegistryImpl.checkAccess(RegistryImpl.java:306)
at sun.rmi.registry.RegistryImpl.bind(RegistryImpl.java:198)
at sun.rmi.registry.RegistryImpl_Skel.dispatch(Unknown Source)
at sun.rmi.server.UnicastServerRef.oldDispatch(UnicastServerRef.java:449)
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:293)
at sun.rmi.transport.Transport$2.run(Transport.java:202)
at sun.rmi.transport.Transport$2.run(Transport.java:199)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Transport.java:198)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:567)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:828)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.access$400(TCPTransport.java:619)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$1.run(TCPTransport.java:684)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$1.run(TCPTransport.java:681)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:681)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
异常是由registry.bind方法抛出来的,查看Java api:
RemoteException - if remote communication with the registry failed; if exception
is a ServerException containing an AccessException, then the registry denies the
caller access to perform this operation (if originating from a non-local host, for example)
由于RMI不支持远程绑定,导致服务注册与服务提供者高度耦合,所以无法实现高可用。要让RMI实现高可用,必须借助像zookeeper这样的分布式协调系统。