很多时候我们的程序都不是单独只在一台机器上运行的,比如需要大量运算我们可以交给其它机器计算,计算完之后把最后的结果返回给我们,这样即使我们本地的机器配置不高,也可以进行大量的计算。还有就是,很多时候,我们并不希望我们所有的代码都放在客户机上运行,而是在需要的时候调用服务器上的方法。这个时候就需要用到远程调用。今天,我就带领大家通过java的rmi来实现远程调用。
首先,我先大概总结一下远程调用实现的基本步骤。然后再看具体的小例子。
一、在服务器端:
1、提供rmi远程方法调用的业务接口,声明须远程访问的方法(方法须throws RemoteException)。继承rmi的Remote接口。
2、创建一个类,该类须继承UnicastRemoteObject implements 业务接口。然后实现相应的业务方法。
3、发布。注册端口(LocateRegistry.createRegistry())。调用Naming.bing()方法绑定接口。
到此,我们服务器端的工作就算完成了。接下来,我们来看客户端:
二、在客户端:
1、提供与服务端相同的接口(注:包名必须相同!!)。
2、通过Naming.lookup()返回服务端对象,通过对象调用服务端方法。
好的,完成上述步骤之后,我们的服务器客户端就算是搭建好了。接下来我们所要做的就是在上面的框架之上去实现相应的业务功能就行了。
下面我们就来看一个具体的小例子:
首先我们还是先来实现服务器端的代码:
1、创建一个业务接口继承字Remote,由于继承了Remote所以该接口要抛出RemoteException异常。该接口声明了一个方法,该方法的返回值是一个二维数组。
package com.pzhu.zj.rmi.server;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface GameUtils extends Remote{
int[][] getMap() throws RemoteException;
}
2、创建一个具体的类,该类继承自UnicastRemoteObject并且实现上面声明的业务接口。具体实现业务方法,在这里就是返回一个二位数组。注:这个类里的代码有点多,你不必在意具体怎么实现的,你只需知道getMap()方法会返回一个二维数组就行了!
package com.pzhu.zj.rmi.server;
import java.rmi.RemoteException;
import java.rmi.server.RemoteObject;
import java.rmi.server.UnicastRemoteObject;
import java.util.Random;
public class GameUtilsServerImp extends UnicastRemoteObject implements GameUtils{
protected GameUtilsServerImp() throws RemoteException {
super();
}
@Override
public int[][] getMap() throws RemoteException {
int[][] a = new int[10][10];
int[] b = new int[64];
int[] dist = new int[64];
Random random = new Random();
for(int i=0;i<b.length;i+=2)
{
int randomNum = Math.abs(random.nextInt())%29 + 1;
b[i] = randomNum;
b[i+1] = randomNum;
}
for(int i=0,length=b.length;i<b.length;i++)
{
int x;
x = Math.abs(random.nextInt());
x = x%length;
dist[i] = b[x];
b[x] = b[--length];
}
int index = 0;
for(int i=1;i<a.length-1;i++)
{
for(int j=1;j<a[i].length-1;j++)
{
a[i][j] = dist[index++];
}
}
return a;
}
}
3、最后一步:发布。发布的时候需要调用LocatRegistry.createRegistry(端口号)注册一个端口号。然后调用Naming.bing("rmi://本地:端口号/标示符",具体类的对象)方法绑定。
package com.pzhu.zj.rmi.server;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
public class ServerPublisher {
public static void main(String[] args) {
try {
LocateRegistry.createRegistry(6666);
Naming.bind("rmi://127.0.0.1:6666/GameUtilsServer", new GameUtilsServerImp());
} catch (Exception e) {
e.printStackTrace();
}
}
}
好的,现在我们的服务器端已经搭建好了。下面我们来做客服端。
1、实现与服务器端相同的业务接口要求包名必须相同,(注:在创建包名的时候一定要仔细)包创建好之后把业务接口拷贝到该包下。由于代码都是一样的,这里就不再粘贴代码了。我把我的目录结构给大家看一下:
![目录结构](https://img-blog.csdn.net/20150410112333935)
2、一切就绪,我们现在可以在main方法中去调用远程方法来获取二维数组了。在这里调用的是Naming.lookup("rmi://服务器IP地址:端口号/标示符");
package com.pzhu.zj.rmi.client
import java.net.MalformedURLException
import java.rmi.Naming
import java.rmi.NotBoundException
import java.rmi.RemoteException
import com.pzhu.zj.rmi.server.GameUtils
import com.pzhu.zj.rmi.server.Test
public class ClientMain {
public static void main(String[] args) {
try {
GameUtils gameUtils = (GameUtils) Naming
.lookup("rmi://服务器IP地址:6666/GameUtilsServer")
int[][] map = gameUtils.getMap()
for(int i=0
{
System.out.println()
for(int j=0
{
System.out.print(map[i][j] + "\t")
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace()
}
}
}
OK到此为止,一个简单的服务器客户机的代码框架就基本完成了。开启服务器之后,运行客户端的程序,客户端将调用服务器上的方法返回一个二维数组,并打印在控制台。
最后再讲讲运用这种模式的好处吧。第一、当我们有些代码不希望放到客户端的时候,这种模式能很好的解决问题。第二、对于代码的维护与更新相当的方便,因为不需要更改客户端的任何代码,只要更新服务器上的代码客户端在下次启动的时候调用的就是更新之后的代码。