很多時候我們的程序都不是單獨只在一臺機器上運行的,比如需要大量運算我們可以交給其它機器計算,計算完之後把最後的結果返回給我們,這樣即使我們本地的機器配置不高,也可以進行大量的計算。還有就是,很多時候,我們並不希望我們所有的代碼都放在客戶機上運行,而是在需要的時候調用服務器上的方法。這個時候就需要用到遠程調用。今天,我就帶領大家通過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到此爲止,一個簡單的服務器客戶機的代碼框架就基本完成了。開啓服務器之後,運行客戶端的程序,客戶端將調用服務器上的方法返回一個二維數組,並打印在控制檯。
最後再講講運用這種模式的好處吧。第一、當我們有些代碼不希望放到客戶端的時候,這種模式能很好的解決問題。第二、對於代碼的維護與更新相當的方便,因爲不需要更改客戶端的任何代碼,只要更新服務器上的代碼客戶端在下次啓動的時候調用的就是更新之後的代碼。