You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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);
}

额外检查项

  1. 确保NodeInterface继承自java.rmi.Remote,且所有方法都声明抛出java.rmi.RemoteException(或其父类Exception)。
  2. 修正Node类构造函数的笔误:public Nodo(String name, String registry)应改为public Node(String name, String registry),否则无法编译。

内容的提问来源于stack exchange,提问作者Cardstdani

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 06:05:00