Java RMI 第一次调用很慢
Java RMI call slow first time
我正在为学校做一个个人项目,我必须使用 RMI 在服务器和客户端之间进行通信。
项目信息
我的项目的目标是在特定时间(纽约证券交易所关闭后)在服务器上检索每天的股票信息(从纽约证券交易所)。每个库存对象都保存在数据库中。信息通过http检索,与RMI无关。
对于客户来说,也可以获取股票。当用户想要获取当天的股票对象时,直接从第 3 方服务中获取。例如,当用户想要获取 Google 上个月的股票时,会通过 RMI 在服务器上请求它。服务器将在数据库中查找股票对象并检索股票对象并将其发送给客户端。
问题
当我启动客户端应用程序时,我必须登录。客户端将创建一个包含用户名和密码的用户对象。
当我按下登录按钮时,大约需要 2 分钟才会显示主屏幕。
在我设置 RMI 连接的源代码下方。
服务器 (main.java)
public static void main(String[] args) throws UnknownHostException {
InetAddress IP= InetAddress.getLocalHost();
System.out.println("IP of my system is := "+IP.getHostAddress());
if(args.length == 1 && args[0].toLowerCase().equals("local")) {
System.out.println("Running on localhost");
System.setProperty("java.rmi.server.hostname", IP.getHostAddress());
} else {
System.out.println("rmi hostname is set to 37.97.223.70");
System.setProperty("java.rmi.server.hostname", "37.97.223.70");
}
try {
Registry reg = LocateRegistry.createRegistry(1099);
StockAppServer server = StockAppServer.getInstance();
reg.rebind("StockApp", server);
System.out.println("StockApp bound for StockAppServer object.");
} catch (RemoteException e) {
e.printStackTrace();
}
}
根据应用程序启动时传递给它的参数,我将 RMI 主机名设置为我当前的 IP 地址或远程服务器地址。远程服务器地址是静态IP,不会变。
服务器 (StockAppServer.java)
这个class实现了客户端用来调用服务器方法的接口。所以这个 class 扩展了 UnicastRemoteObject。当我启动服务器时,将调用 registerStockTask()。此方法将获取股票代码 (What are ticker symbols?),然后安排任务在特定时间获取所有股票对象。
private static StockAppServer _instance;
private List<User> loggedInUsers;
private List<Group> activeGroups;
private List<Notification> registeredNotifications;
private StockAppServer() throws IOException {
_instance = this;
this.loggedInUsers = new ArrayList<>();
this.activeGroups = new ArrayList<>();
this.registeredNotifications = new ArrayList<>();
this.registerStockTask();
clearActiveGroups();
checkForCompletedNotifications();
// Start the restful framework to allow incoming connections from the NodeJS server to manage new notification
Router.getInstance();
}
public static StockAppServer getInstance() {
try{
return _instance == null ? new StockAppServer() : _instance;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
客户端 (main.java)
public static void main(String[] arguments) throws Exception {
args = arguments;
Application.launch();
}
@Override
public void start(Stage primaryStage) throws Exception {
InetAddress IP= InetAddress.getLocalHost();
System.out.println("IP of my system is := "+IP.getHostAddress());
if(args.length == 1 && args[0].toLowerCase().equals("local")) {
// Program started with local command, expect that server is running on local host
reg = LocateRegistry.getRegistry(IP.getHostAddress(), 1099);
System.out.println("Attempting to connect to RMI server over 127.0.0.1");
} else {
// Program started without additional commands. Except that "the server" is available;
reg = LocateRegistry.getRegistry("37.97.223.70", 1099);
System.out.println("Attempting to connect to RMI server over 37.97.223.70");
}
try {
StockApp.getInstance().setServerInterfaces((IStockSend) reg.lookup("StockApp"), (IUserHandling) reg.lookup("StockApp"));
} catch(RemoteException e) {
AlertMessage.showException("Unable to connect to server.", e);
} catch (NotBoundException e) {
AlertMessage.showException("No server has been found with the name \"StockApp\" on the remote host.\nPlease try again later", e);
}
LoginController.showMenu();
//FileNotFoundException e = new FileNotFoundException("Couldn't find file blabla.txt");
//AlertMessage.showException("Something went wrong. Please try again later.", e);
}
我是如何尝试解决问题的
当我在本地测试我的应用程序时,没有问题。登录方法将在几毫秒内完成,我将代表主屏幕。
- 我首先关闭了 macbook 上的防火墙。没有结果,登录方法仍然需要2秒左右。
- 我关闭了 Ubuntu 服务器的防火墙。没有结果,服务器和macbook上的防火墙都关闭了。登录方法仍然需要大约 2 秒。
- 在服务器上 运行s(感谢 jenkins)另一个(不相关的)程序。该程序使用套接字而不是 RMI。当这个程序不是运行ning时,登录方法仍然需要大约2分钟。
在StockAppServer.java中,我调用了如下方法:
超级(1099);
这与我采取的上述步骤具有相同的结果。
我不知道我还能尝试什么来解决我的问题。
我试图为 RMI 部分提供尽可能多的代码。如果您需要任何其他源代码,请询问,我可以更新此问题。此外,源代码可通过 github: https://github.com/juleskreutzer/GSO-Maatwerk 获得。确保 运行 带有 -remote 参数的程序。
更新 1 (9-1-2017)
根据评论中yanys的要求,我应该运行执行以下命令:
dscacheutil -q host -a name localhost
这个returns下面的输出:
Mac:
name: localhost
ip_address: 127.0.0.1
Ubuntu:
dscacheutil: command not found
更新 2 (9-1-2017)
我与 VPS 的供应商核对过 运行 java 服务器所在的供应商。在他们这边,一切都应该没问题。按照他们的说法,应该不是dns的问题。经过一些研究,我发现 RMI 同时使用 DNS 和反向 DNS。在这种情况下,反向 DNS 就是问题所在。请参阅我的回答,了解我是如何解决我的问题的。
正如 EJP 在对该问题的评论中指出的那样,是 DNS 问题。
我联系了我的托管服务提供商的支持,看看我是否有错误的设置。他们帮我解决了这个问题。
首先我们测试了我的 VPS 的速度,这是大约 1000mbit 的下载和上传速度。我们检查过后,他们说他们这边没有问题。
经过一些研究,我发现 RMI 同时使用 DNS 和反向 DNS。问题是我没有在我的服务器上设置反向 DNS。我已经有一个域名用于反向 DNS。
我做了以下事情:
- 在我的网站上创建一个指向服务器 IP 地址的 A 记录。我把它命名为vps.mydomain.com
- 在我的服务器的控制面板中添加反向 DNS
- 将我的服务器的主机名更改为 vps.mydomain.com*
*我的服务器运行 Ubuntu 16.04,在 ubuntu 有 systemd 的机器上,你可以使用命令
sudo hostnamectl set-hostname new-name
更改主机名
我正在为学校做一个个人项目,我必须使用 RMI 在服务器和客户端之间进行通信。
项目信息
我的项目的目标是在特定时间(纽约证券交易所关闭后)在服务器上检索每天的股票信息(从纽约证券交易所)。每个库存对象都保存在数据库中。信息通过http检索,与RMI无关。
对于客户来说,也可以获取股票。当用户想要获取当天的股票对象时,直接从第 3 方服务中获取。例如,当用户想要获取 Google 上个月的股票时,会通过 RMI 在服务器上请求它。服务器将在数据库中查找股票对象并检索股票对象并将其发送给客户端。
问题
当我启动客户端应用程序时,我必须登录。客户端将创建一个包含用户名和密码的用户对象。 当我按下登录按钮时,大约需要 2 分钟才会显示主屏幕。
在我设置 RMI 连接的源代码下方。
服务器 (main.java)
public static void main(String[] args) throws UnknownHostException {
InetAddress IP= InetAddress.getLocalHost();
System.out.println("IP of my system is := "+IP.getHostAddress());
if(args.length == 1 && args[0].toLowerCase().equals("local")) {
System.out.println("Running on localhost");
System.setProperty("java.rmi.server.hostname", IP.getHostAddress());
} else {
System.out.println("rmi hostname is set to 37.97.223.70");
System.setProperty("java.rmi.server.hostname", "37.97.223.70");
}
try {
Registry reg = LocateRegistry.createRegistry(1099);
StockAppServer server = StockAppServer.getInstance();
reg.rebind("StockApp", server);
System.out.println("StockApp bound for StockAppServer object.");
} catch (RemoteException e) {
e.printStackTrace();
}
}
根据应用程序启动时传递给它的参数,我将 RMI 主机名设置为我当前的 IP 地址或远程服务器地址。远程服务器地址是静态IP,不会变。
服务器 (StockAppServer.java)
这个class实现了客户端用来调用服务器方法的接口。所以这个 class 扩展了 UnicastRemoteObject。当我启动服务器时,将调用 registerStockTask()。此方法将获取股票代码 (What are ticker symbols?),然后安排任务在特定时间获取所有股票对象。
private static StockAppServer _instance;
private List<User> loggedInUsers;
private List<Group> activeGroups;
private List<Notification> registeredNotifications;
private StockAppServer() throws IOException {
_instance = this;
this.loggedInUsers = new ArrayList<>();
this.activeGroups = new ArrayList<>();
this.registeredNotifications = new ArrayList<>();
this.registerStockTask();
clearActiveGroups();
checkForCompletedNotifications();
// Start the restful framework to allow incoming connections from the NodeJS server to manage new notification
Router.getInstance();
}
public static StockAppServer getInstance() {
try{
return _instance == null ? new StockAppServer() : _instance;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
客户端 (main.java)
public static void main(String[] arguments) throws Exception {
args = arguments;
Application.launch();
}
@Override
public void start(Stage primaryStage) throws Exception {
InetAddress IP= InetAddress.getLocalHost();
System.out.println("IP of my system is := "+IP.getHostAddress());
if(args.length == 1 && args[0].toLowerCase().equals("local")) {
// Program started with local command, expect that server is running on local host
reg = LocateRegistry.getRegistry(IP.getHostAddress(), 1099);
System.out.println("Attempting to connect to RMI server over 127.0.0.1");
} else {
// Program started without additional commands. Except that "the server" is available;
reg = LocateRegistry.getRegistry("37.97.223.70", 1099);
System.out.println("Attempting to connect to RMI server over 37.97.223.70");
}
try {
StockApp.getInstance().setServerInterfaces((IStockSend) reg.lookup("StockApp"), (IUserHandling) reg.lookup("StockApp"));
} catch(RemoteException e) {
AlertMessage.showException("Unable to connect to server.", e);
} catch (NotBoundException e) {
AlertMessage.showException("No server has been found with the name \"StockApp\" on the remote host.\nPlease try again later", e);
}
LoginController.showMenu();
//FileNotFoundException e = new FileNotFoundException("Couldn't find file blabla.txt");
//AlertMessage.showException("Something went wrong. Please try again later.", e);
}
我是如何尝试解决问题的
当我在本地测试我的应用程序时,没有问题。登录方法将在几毫秒内完成,我将代表主屏幕。
- 我首先关闭了 macbook 上的防火墙。没有结果,登录方法仍然需要2秒左右。
- 我关闭了 Ubuntu 服务器的防火墙。没有结果,服务器和macbook上的防火墙都关闭了。登录方法仍然需要大约 2 秒。
- 在服务器上 运行s(感谢 jenkins)另一个(不相关的)程序。该程序使用套接字而不是 RMI。当这个程序不是运行ning时,登录方法仍然需要大约2分钟。
在StockAppServer.java中,我调用了如下方法:
超级(1099); 这与我采取的上述步骤具有相同的结果。
我不知道我还能尝试什么来解决我的问题。
我试图为 RMI 部分提供尽可能多的代码。如果您需要任何其他源代码,请询问,我可以更新此问题。此外,源代码可通过 github: https://github.com/juleskreutzer/GSO-Maatwerk 获得。确保 运行 带有 -remote 参数的程序。
更新 1 (9-1-2017)
根据评论中yanys的要求,我应该运行执行以下命令:
dscacheutil -q host -a name localhost
这个returns下面的输出:
Mac:
name: localhost
ip_address: 127.0.0.1
Ubuntu:
dscacheutil: command not found
更新 2 (9-1-2017)
我与 VPS 的供应商核对过 运行 java 服务器所在的供应商。在他们这边,一切都应该没问题。按照他们的说法,应该不是dns的问题。经过一些研究,我发现 RMI 同时使用 DNS 和反向 DNS。在这种情况下,反向 DNS 就是问题所在。请参阅我的回答,了解我是如何解决我的问题的。
正如 EJP 在对该问题的评论中指出的那样,是 DNS 问题。
我联系了我的托管服务提供商的支持,看看我是否有错误的设置。他们帮我解决了这个问题。
首先我们测试了我的 VPS 的速度,这是大约 1000mbit 的下载和上传速度。我们检查过后,他们说他们这边没有问题。
经过一些研究,我发现 RMI 同时使用 DNS 和反向 DNS。问题是我没有在我的服务器上设置反向 DNS。我已经有一个域名用于反向 DNS。
我做了以下事情:
- 在我的网站上创建一个指向服务器 IP 地址的 A 记录。我把它命名为vps.mydomain.com
- 在我的服务器的控制面板中添加反向 DNS
- 将我的服务器的主机名更改为 vps.mydomain.com*
*我的服务器运行 Ubuntu 16.04,在 ubuntu 有 systemd 的机器上,你可以使用命令
sudo hostnamectl set-hostname new-name
更改主机名