摘 要:網(wǎng)格計(jì)算是分布式計(jì)算的一種方式,它的目的是要實(shí)現(xiàn)虛擬組織中資源(如CPU、存儲(chǔ)器等)的共享。針對(duì)計(jì)算密集型應(yīng)用中傳統(tǒng)的分布式計(jì)算模式的不足,利用Java程序的跨平臺(tái)特性,提出了網(wǎng)格計(jì)算中代碼上載的思想,并給出了其一個(gè)模型的設(shè)計(jì)和實(shí)現(xiàn)。
關(guān)鍵詞:網(wǎng)格計(jì)算 計(jì)算密集 Java 代碼上載
中圖分類號(hào):TP302.1 文獻(xiàn)標(biāo)識(shí)碼:A 文章編號(hào):1673-8454(2008)23-0022-03
根據(jù)共享資源的不同,網(wǎng)格計(jì)算可以分為計(jì)算密集型和數(shù)據(jù)密集型等(即計(jì)算網(wǎng)格和數(shù)據(jù)網(wǎng)格)。計(jì)算網(wǎng)格對(duì)CPU和存儲(chǔ)器資源要求較高,而計(jì)算中需要的數(shù)據(jù)較少。[1] 傳統(tǒng)的分布式計(jì)算技術(shù),例如CORBA,RMI和DCOM中資源共享通常局限于一個(gè)固定的組織,這種共享是靜態(tài)的,并且開(kāi)銷較大。[2] 本文針對(duì)計(jì)算密集型應(yīng)用中傳統(tǒng)的分布式計(jì)算模式的不足,提出了網(wǎng)格計(jì)算中代碼上載的思想,并給出了一個(gè)支持代碼上載的網(wǎng)格模型的設(shè)計(jì)和實(shí)現(xiàn)。
一、網(wǎng)格模型設(shè)計(jì)
支持代碼上載的網(wǎng)格模型如圖1所示。為了實(shí)現(xiàn)網(wǎng)格服務(wù)的透明性,本模型設(shè)計(jì)中在用戶所在的機(jī)器上設(shè)置了一個(gè)用戶代理。
用戶代理具有以下職責(zé):(1)與用戶交互:接收用戶執(zhí)行某程序代碼請(qǐng)求,最后將計(jì)算結(jié)果呈現(xiàn)給用戶;(2)與信息服務(wù)交互:根據(jù)用戶提供的要求向信息服務(wù)查詢,并收到符合要求的計(jì)算引擎的IP地址和服務(wù)端口;(3)與計(jì)算引擎交互,將用戶提交的程序代碼傳送給計(jì)算引擎,并接收計(jì)算結(jié)果。
每當(dāng)有一個(gè)新的計(jì)算引擎啟動(dòng)時(shí),它就會(huì)自動(dòng)連接信息服務(wù),并將它的信息發(fā)送給信息服務(wù),并請(qǐng)求在信息數(shù)據(jù)庫(kù)中注冊(cè);當(dāng)有用戶代理查詢符合某要求的計(jì)算引擎時(shí),信息服務(wù)根據(jù)該要求從信息數(shù)據(jù)庫(kù)中查詢和選擇負(fù)載較輕的計(jì)算引擎,并將該引擎的IP地址和端口發(fā)送給用戶代理;當(dāng)某計(jì)算引擎的當(dāng)前負(fù)載狀況等特征改變時(shí),它也將這些改變通知信息服務(wù),信息服務(wù)及時(shí)更新信息數(shù)據(jù)庫(kù)中的相關(guān)信息。這樣信息數(shù)據(jù)庫(kù)就實(shí)時(shí)地動(dòng)態(tài)反映了網(wǎng)格環(huán)境中的計(jì)算資源。
本模型的實(shí)現(xiàn)基于客戶/服務(wù)器模式,利用了現(xiàn)有的廣泛存在的TCP/IP通信協(xié)議,并采用Java語(yǔ)言進(jìn)行開(kāi)發(fā)。
二、實(shí)現(xiàn)細(xì)節(jié)
由于篇幅有限,本文只介紹計(jì)算引擎、用戶代理和相關(guān)輔助類的實(shí)現(xiàn),并且假定用戶代理已經(jīng)從信息服務(wù)那里查詢到了計(jì)算引擎所在的IP地址和服務(wù)端口。
1.輔助類的實(shí)現(xiàn)
用戶代理將編譯過(guò)的用戶程序(擴(kuò)展名為class)發(fā)送給計(jì)算引擎,計(jì)算引擎接收該代碼文件并存于本地,然后JVM中的解釋器加載該代碼文件中的類并執(zhí)行。大致框架如下:
Class c = cl.loadClass(classname,bytecode, 0, bytecode.length);
Object o=c.newInstance();
((SomeInterface)o).someClassMethod();
不能將引用 o 轉(zhuǎn)換到someClass,因?yàn)檫@時(shí)計(jì)算引擎還不直接知道傳送來(lái)的類名(通過(guò)后面所講的類Task的屬性TaskName可以間接知道)。為此用戶程序必須繼承一個(gè)抽象類或?qū)崿F(xiàn)一個(gè)接口(這里用接口),并且在用戶代理和計(jì)算引擎所在的機(jī)器上部署該接口,由于接口名稱是事先已知的,從而可以動(dòng)態(tài)執(zhí)行用戶代理傳來(lái)的任何用戶代碼。接口定義Skeleton.java如下:
import java.net.*;
public interface Skeleton {
void run(Socket s);}
用戶程序類必須實(shí)現(xiàn)該接口,給出方法run的具體實(shí)現(xiàn)。假設(shè)用戶程序類用來(lái)計(jì)算fibonacci數(shù)列中的第40項(xiàng),則用戶程序類Compute.java的代碼如下(本文代碼省略了相應(yīng)的import語(yǔ)句):
public class Computeimplements Skeleton{
public void run(Socket s){
DataOutputStream os = 1;
try{
os = new DataOutputStream(s.getOutputStream());
long a=fib(40);
String output=\"The fib(40) is \"+a;
System.out.println(output);
os.writeBytes(output); }
catch(IOException e) {
System.out.println(\"Erorr io: \"+e);}
}
public long fib(long n)
{if (n<3)
return(1);
else
return(fib(n-2)+fib(n-1));}
}
還有一個(gè)輔助類Task.java,它封裝了一個(gè)字符串和byte數(shù)組,用來(lái)傳送用戶的代碼文件,由于要通過(guò)Socket發(fā)送Task對(duì)象,所以類Task必須實(shí)現(xiàn)Serializable接口。Task.java的代碼如下:
public class Task implements Serializable{
public String TaskName;
public byte ByteCode[];
public Task (String n,byte b[])
{this.TaskName=n;
this.ByteCode=b;}
}
和Skeleton.java一樣,必須將Task.java編譯后得到的Task.class部署在用戶代理和計(jì)算引擎所在的機(jī)器上。
2.用戶代理的實(shí)現(xiàn)
用戶代理程序非常簡(jiǎn)單,它所要做的就是接收用戶提交的代碼文件名,打開(kāi)一個(gè)到位于指定IP地址和服務(wù)端口的計(jì)算引擎的連接,用一個(gè)Task對(duì)象封裝該代碼文件,并通過(guò)Socket發(fā)送該對(duì)象。最后接收計(jì)算引擎返回的結(jié)果,并在控制臺(tái)上將結(jié)果呈現(xiàn)給用戶。用戶代理程序可以基于GUI實(shí)現(xiàn),為了簡(jiǎn)單和說(shuō)明方便,這里給出基于命令行的實(shí)現(xiàn),代碼UserAgent.java如下:
public class UserAgent {
public static void main(String[] argv)
{ public String IP=”202.114.20.44”;
publice SERVICE_PORT=4000;
Socket s=1;
ObjectOutputStream os=1;
BufferedReader is = 1;
String output=1;
try {
s = new Socket(IP, SERVICE_PORT);
os = new ObjectOutputStream(s.getOutputStream());
is=new BufferedReader(new InputStreamReader(s.getInputStream()));
System.out.println(\"transimiting file....\");
// 讀取用戶提交的代碼文件,并將它封裝在一個(gè)Task對(duì)象中。
String filename=argv[0];
int l=filename.length();
FileInputStream fis = new FileInputStream(filename);
byte[] bytecode = new byte[fis.available()];
fis.read(bytecode);
Task obj = new Task(filename, bytecode);
// 通過(guò)Socket發(fā)送Task對(duì)象
os.writeObject(obj);
//接收計(jì)算引擎發(fā)送來(lái)的計(jì)算結(jié)果,并在控制臺(tái)上顯示
output=is.readLine();
System.out.println(\"Got from server: \"+output);
fis.close();
os.close();
s.close();
} catch (Exception e) {
e.printStackTrace(); }
}}
這里使用Socket類建立了基于TCP的網(wǎng)絡(luò)連接是為了使用戶代理和計(jì)算引擎之間的通信更可靠。
3.計(jì)算引擎的實(shí)現(xiàn)
計(jì)算引擎在端口(此處為4000)監(jiān)聽(tīng),使用ServerSocket對(duì)象接受用戶代理的連接。每當(dāng)一個(gè)用戶代理連接到所監(jiān)聽(tīng)的端口,ServerSocket就分配一個(gè)新的Socket對(duì)象,然后將這個(gè)新Socket對(duì)象的引用傳遞給一個(gè)新產(chǎn)生的線程(線程類Connects的一個(gè)實(shí)例)處理,主線程在端口4000繼續(xù)監(jiān)聽(tīng)更多的連接請(qǐng)求,這種在服務(wù)端口的不斷監(jiān)聽(tīng)是通過(guò)一個(gè)While循環(huán)實(shí)現(xiàn)的,代碼Server.java如下所示:
public class Server {
public static final int SERVICE_PORT=4000;
public static void main(String argv[]) throws IOException
{ServerSocket service = new ServerSocket(SERVICE_PORT);
Socket clientSocket;
System.out.println(\"Server is started!\");
while(true){
clientSocket = service.accept();
new Connects(clientSocket);
}}}
線程類Connects的方法run包含了計(jì)算引擎與各個(gè)用戶代理的具體交互操作。首先接收用戶發(fā)送來(lái)的Task對(duì)象,將Task對(duì)象中封裝的代碼文件恢復(fù)成文件的形式;然后從該代碼文件中加載用戶程序類并執(zhí)行;最后將計(jì)算結(jié)果發(fā)送給用戶代理。線程類的代碼Connects.java如下所示:
class Connects extends Thread{
Socket client;
ObjectInputStream ois ;
FileOutputStream fos;
public Connects(Socket s){
client = s;
this.start();}
public void run(){
try {
CL cl=new CL();
ois = new ObjectInputStream(client.getInputStream());
// 通過(guò)Socket讀取Task對(duì)象,并還模得到完整的代碼文件
Task obj = (Task) ois.readObject();
byte bytecode[] = obj.ByteCode;
String filename = obj.TaskName;
int l=filename.length();
String classname=filename.substring(0,l-6);
fos = new FileOutputStream(filename);
fos.write(bytecode);
fos.close();
System.out.println(\"the file \"+filename+\" is received!\");
// 從代碼文件加載類并執(zhí)行其中的方法run
Class c = cl.loadClass(classname,bytecode, 0, bytecode.length);
Object o=c.newInstance();
((Skeleton) o).run(client);
}
catch(Exception e) {
e.printStackTrace();
}
try {
ois.close();
client.close();}
catch(IOException ex) {
ex.printStackTrace();}
}}
class CL extends ClassLoader{
public Class loadClass(String name, byte[] b, int off,int len)
{ return super.defineClass(name,b,off,len);}}
其中的類CL是ClassLoader類的子類,它的方法loadClass用來(lái)加載類,之所以不直接使用ClassLoader類的方法defineClass是因?yàn)镃lassLoader是抽象類,不能實(shí)例化。
4.代碼執(zhí)行
啟動(dòng)計(jì)算引擎后,執(zhí)行用戶代理的代碼如圖2所示(Compute.class是用戶代碼文件名)。
三、結(jié)論
本文提出的計(jì)算網(wǎng)格模型的設(shè)計(jì)及其實(shí)現(xiàn),利用了Java程序跨平臺(tái)執(zhí)行的特性和現(xiàn)有的廣泛存在的TCP/IP的通信協(xié)議,它可以隨時(shí)利用計(jì)算網(wǎng)格中高性能計(jì)算機(jī)的CPU和內(nèi)存快速運(yùn)算處理用戶各種特定的計(jì)算密集型應(yīng)用,實(shí)現(xiàn)了跨組織的計(jì)算資源的動(dòng)態(tài)共享。
參考文獻(xiàn):
[1]I. Foster, C. KesselmanThe Grid: Blueprint for a New Computing Infrastructure [M] Morgan Kaufmann Pub.,1998
[2] I. Foster, C. Kesselman, S.TueckeThe Anatomy of the Grid [J] Intl.J.Supercomputer Applications,2001
[3]The Globus Project home page, http://www.globus.org
[4]都志輝,陳渝,劉鵬. 網(wǎng)格計(jì)算[M].北京:清華大學(xué)出版社,2002.
[5](美) Bruce Eckel 著 Java編程思想(英文版.第二版)[M].北京:機(jī)械工業(yè)出版社,2002.
[6](澳)David Reilly著,沈鳳 等譯. Java網(wǎng)絡(luò)編程與分布式計(jì)算[M].北京:機(jī)械工業(yè)出版社,2003.