java rmi ip_投机取巧解决多IP时RMI调用的java.rmi.ConnectException

在RMI调用中,有三个角色:Server,Client,Registry。

Server向Registry注册自己提供的服务的连接信息,其中包含Server自己的IP;Client通过Registry获取Server的连接信息,然后连接Server,调用Server提供的服务。

这三者可以在不同的机器上,RMI也是按照这样的场景设计的。虽然经常会遇到Server和Registry在同一个机器上甚至同一个Java进程里情况,尤其是测试的时候。

当Server所在的机器有多个IP并且Client和Server不在同一个机器的时候,问题来了:

Client并不知道Server的IP,它是通过Registry获得Server的IP(这个描述不严谨,实际上Client通过Registry获得的并不只是一个IP)。

而Registry和Server也都无法自动的确定Client应该用Server的哪个IP和Server通讯。

所以Server向Registry注册的自己的连接信息的时候,连接信息里面的IP,只是随便从自己的多个IP里选择了一个。

这个随便选择的IP,不一定是Client能用来和Server通讯的IP。

然后Client连接Server的时候就会ConnectException。

官方文档给的解决方案是设置Server所在的jvm的系统属性java.rmi.server.hostname,然后Client会通过设置的值连接Server。

准确的说是Server向Registry注册自己的连接信息的时候,不再是随便选择一个IP,而是java.rmi.server.hostname的值。

那如果无法在Server端正确设置java.rmi.server.hostname的值,比如Server有两个IP:192.168.1.2和10.0.0.2,然后有两个Client分别使用这两个IP才能和Server通讯,那就不能设置java.rmi.server.hostname为确定的值了。

这样的情况怎么办了?

前面已经说了,Client通过Registry获得Server的IP,这种说法是不严谨的。

实际上,Client获得的是一个java.rmi.server.RMIClientSocketFactory的实例。

参见java.rmi.server.UnicastRemoteObject的构造函数UnicastRemoteObject(int port, RMIClientSocketFactory csf, RMIServerSocketFactory ssf) throws RemoteException

或者静态函数exportObject(Remote obj, int port, RMIClientSocketFactory csf, RMIServerSocketFactory ssf) throws RemoteException

这两个函数里的RMIClientSocketFactory类型的参数,是在Server端创建,然后传到Client端,在Client连接Server的时候调用的。

这意味着,在Client连接Server之前,就已经拿到这个在Server端创建的对象了。

所以可以通过这个对象传递一些信息到Client,以协助Client正确的连接Server。

比如,把Server所有的IP都放到RMIClientSocketFactory对象里,然后在Client连接Server的时候一个一个的试。

像这样:

import java.io.IOException;

import java.io.Serializable;

import java.net.Socket;

import java.rmi.server.RMIClientSocketFactory;

public class RMIClientSocketFactoryImpl implements RMIClientSocketFactory, Serializable {

private static final long serialVersionUID = -2866768131646972083L;

@Override

public Socket createSocket(String host, int port) throws IOException {

return new Socket(host, port);

}

}

import java.io.IOException;

import java.io.Serializable;

import java.net.InetAddress;

import java.net.Socket;

import java.rmi.server.RMIClientSocketFactory;

public class TryReachBeforeCreateSocketFactory implements RMIClientSocketFactory, Serializable {

private static final long serialVersionUID = 270963626626446102L;

private String[] ips;

private RMIClientSocketFactory socketFactory;

private transient String ip = null;

public TryReachBeforeCreateSocketFactory(String[] localIps, RMIClientSocketFactory clientSocketFactory) {

ips = localIps.clone();

socketFactory = clientSocketFactory;

}

@Override

public Socket createSocket(String host, int port) throws IOException {

if(ip != null) {

return socketFactory.createSocket(ip, port);

}

for (int i = 0; i 

String trying = ips[i];

try {

if (InetAddress.getByName(trying).isReachable(3000)) {

Socket socket = socketFactory.createSocket(trying, port);

ip = trying;

return socket;

}

} catch (IOException e) {

}

}

return socketFactory.createSocket(host, port);

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值