无法与远程服务器RMI Registry建立连接的问题排查
我来帮你梳理几个RMI跨网络部署时最容易踩的坑,结合你的代码和Azure环境来看,这些点大概率能解决你的问题:
1. 先确认Registry端口的一致性
看你的服务器代码,构造函数接收了int port参数,但创建Registry时用的是硬编码的PORT常量:
registry = LocateRegistry.createRegistry(PORT);
而客户端连接时用的是SERVER_PORT常量,这两个值必须完全匹配!如果服务器用的是默认1099端口,客户端也要用1099;如果自定义了端口,两边的常量值必须统一。
另外,这里有个高频坑:RMI默认会给远程对象分配随机端口,哪怕你开放了Registry的端口,客户端调用方法时需要连接这个随机端口,而Azure防火墙没开放这些端口,就会超时。这个问题后面单独说解决办法。
2. 修复Naming.bind的URL格式错误
你的服务器代码里这段写法是错的:
Naming.bind(address, this);
Naming.bind要求传入标准的RMI协议URL,格式是rmi://<主机IP>:<端口>/<服务名>。比如你的公网IP是13.xx.xx.xx,Registry端口是1099,服务名是REGISTRY_NAME,应该写成:
Naming.bind("rmi://" + address + ":" + PORT + "/" + REGISTRY_NAME, this);
不过其实你已经通过registry.bind(REGISTRY_NAME, this)把对象绑定到Registry了,完全没必要重复用Naming.bind,重复绑定反而可能引发隐藏异常,建议直接删掉Naming.bind和Naming.rebind的代码块,只用Registry的bind/rebind逻辑就够了。
3. 把java.rmi.server.hostname设为Azure公网IP
你异常里显示的目标IP是10.0.0.6,这是虚拟机的内网IP,外部客户端根本访问不到!必须把java.rmi.server.hostname设置为Azure虚拟机的公网IP地址(你可以在Azure门户的虚拟机详情页找到这个IP),替换掉构造函数传入的address参数。
4. 修复安全策略的启动参数错误
你的启动命令里的安全策略参数写法不对:
-Djava.security.policy=java.security.AllPermission
java.security.policy需要指向一个策略文件,或者用特殊语法临时开放所有权限。正确的两种写法:
- 方法一(推荐,适合测试和生产):创建一个
rmi.policy文件,内容如下:
然后启动命令改为:grant { permission java.security.AllPermission; };java -cp flyway-core-6.0.8.jar:mssql-jdbc-7.4.1.jre8.jar:mysql-connector-java-8.0.18.jar:. -Djava.security.policy=rmi.policy kmalfa.RmiServer - 方法二(仅快速测试用):用
==-(两个等号)直接允许所有权限:java -cp flyway-core-6.0.8.jar:mssql-jdbc-7.4.1.jre8.jar:mysql-connector-java-8.0.18.jar:. -Djava.security.policy==- kmalfa.RmiServer
错误的策略配置可能导致RMI无法正确暴露远程对象,哪怕服务器看起来启动成功。
5. 固定RMI远程对象的端口(解决随机端口问题)
前面提到的随机端口问题,你可以通过启动参数强制固定这个端口:
在服务器启动命令里添加:
-Djava.rmi.server.port=<你指定的固定端口,比如1100>
然后在Azure的网络安全组(NSG)里,把这个固定端口和Registry端口(比如1099)一起加入入站规则,允许外部访问。这样客户端调用方法时就会连接这个固定端口,不会再出现超时。
6. 验证服务器端的Registry注册状态
你可以在服务器代码里加一段打印,确认服务是否真的注册成功:
import java.util.Arrays; // 在绑定完成后添加 String[] boundServices = registry.list(); System.out.println("已注册的服务列表:" + Arrays.toString(boundServices));
如果能看到REGISTRY_NAME在列表里,说明Registry注册是正常的。也可以在虚拟机上用telnet localhost <Registry端口>测试本地是否能连通Registry。
7. 最后确认Azure的网络配置
- 确保虚拟机的**网络安全组(NSG)**入站规则,已经开放了Registry端口和你固定的RMI对象端口,并且源IP允许你的客户端IP(或者临时设为所有IP测试)。
- 确认虚拟机的公网IP是静态IP,避免重启后IP变化导致客户端连接失败。
按照上面的步骤逐一排查,应该就能解决你的连接问题了。
内容的提问来源于stack exchange,提问作者Narrangel




