Java RMI与LDAP分布式系统远程方法执行异常问题
问题分析与解决方案
问题根源
你直接将Node实例(服务器端对象)绑定到LDAP,而LDAP存储的是该对象的服务器端引用(UnicastServerRef),而非客户端可用的远程存根(Stub)。当其他客户端通过lookup获取对象时,反序列化得到的是本地进程内的对象引用,导致method2()调用在本地执行,而非远程节点。
从你提供的远程对象输出:
Node[UnicastServerRef [liveRef: [endpoint:[192.168.56.1:60014](local),objID:[-53f66d6:18f160a3009:-7ffe, 6389968686518309714]]]]
可以明确看出,返回的是本地服务器端引用,而非远程存根。
解决方案
方案1:通过LDAP存储RMI引用URL(推荐)
这种方式符合RMI与LDAP协同的标准用法,避免直接序列化远程对象带来的问题:
修改Launcher类(先绑定到RMI注册表,再存储URL到LDAP)
public class Launcher { public static void main(String[] args) throws Exception { String rmiRegistryAddress = args[0]; // 格式:host:port,例如192.168.56.1:1099 String nodeName = args[1]; String ldapRegistry = args[2]; // 拆分RMI注册表的主机和端口 String[] rmiParts = rmiRegistryAddress.split(":"); String rmiHost = rmiParts[0]; int rmiPort = Integer.parseInt(rmiParts[1]); // 创建并导出Node远程对象 Node newNode = new Node(nodeName, rmiRegistryAddress); // 绑定到RMI注册表 Registry rmiRegistry = LocateRegistry.getRegistry(rmiHost, rmiPort); rmiRegistry.rebind(nodeName, newNode); // 连接LDAP并存储RMI引用URL DirContext ldapContext = Utils.createLDAPContext(ldapRegistry); registerNodeRmiUrl(nodeName, rmiHost, rmiPort, ldapContext); } private static void registerNodeRmiUrl(String nodeName, String rmiHost, int rmiPort, DirContext ldapContext) throws Exception { Attributes attributes = new BasicAttributes(); attributes.put(new BasicAttribute("objectClass", "javaContainer")); attributes.put(new BasicAttribute("cn", nodeName)); // 存储RMI远程对象的URL String rmiUrl = "rmi://" + rmiHost + ":" + rmiPort + "/" + nodeName; attributes.put(new BasicAttribute("javaReferenceAddress", rmiUrl)); ldapContext.rebind("cn=" + nodeName + ",ou=Nodes", null, attributes); System.out.println("Node registered successfully with name: " + nodeName); } }
修改Node类的method方法(从LDAP取URL再获取远程对象)
public void method(String ldapRegistry, String remoteNodeName) throws Exception { DirContext ldapContext = Utiles.createLDAPContext(ldapRegistry); // 从LDAP获取RMI URL Attributes attrs = ldapContext.getAttributes("cn=" + remoteNodeName + ",ou=Nodes", new String[]{"javaReferenceAddress"}); String rmiUrl = attrs.get("javaReferenceAddress").get().toString(); // 通过RMI URL获取远程对象存根 Node remote = (Node) Naming.lookup(rmiUrl); remote.method2("Message"); }
方案2:直接绑定远程对象的存根到LDAP
如果坚持直接存储对象,可手动获取远程对象的存根后再绑定到LDAP:
修改Launcher的registerNode方法
private static void registerNode(String nodeName, Node node, DirContext ldapContext) throws Exception { // 手动获取远程对象的存根 Remote stub = UnicastRemoteObject.toStub(node); Attributes attributes = new BasicAttributes(); attributes.put(new BasicAttribute("objectClass", "javaContainer")); attributes.put(new BasicAttribute("cn", nodeName)); // 绑定存根到LDAP,而非原始Node实例 ldapContext.rebind("cn=" + nodeName + ",ou=Nodes", stub, attributes); System.out.println("Node registered successfully with name: " + nodeName); }
额外检查项
- 确保
NodeInterface继承自java.rmi.Remote,且所有方法都声明抛出java.rmi.RemoteException(或其父类Exception)。 - 修正Node类构造函数的笔误:
public Nodo(String name, String registry)应改为public Node(String name, String registry),否则无法编译。
内容的提问来源于stack exchange,提问作者Cardstdani
相关产品推荐
相关产品推荐

