虽然移动Agent的实质是移动代码与代码运行挂起时所处的状态,也就是对象从一台主机移动到另一台主机上,我们自己就能实现,例如在JAVA中可以通过JAVA的序列化机制来实现,在C++中我们也可以用DCOM来实现。但是要实现移动Agent,为了加快开发节省成本我们一般要选择一个成熟的移动Agent开发平台。移动Agent开发平台为我们开发基于移动Agent的程序提供了各种公共服务。按照现有的实现技术可以将移动Agent系统分为两类:一种是基于移动代码的,例如Telescript;另一种是基于远程对象的,例如Aglet。下面对当前较为成熟的移动Agent系统平台的分析与对比如下:
1.D’Agent
D’Agent(以前称为Agent TCL)是美国Dartmouth大学研究的移动Agent系统。它从支持单一语言TCL发展到目前支持TCL,JAVA和Scheme等多种语言。D’Agent能自动捕获和恢复移动Agent的完整状态,即意味这代理可以在任意点中断执行,然后移动到其它环境中继续执行。
通讯基础: D’Agent的高层通讯机制采用RMI,KiaML等不同的通讯方式,它的服务程序是多线程的,每个代理在单独的进程中执行,这种方式简化了实现,但增加了进程间的通讯开销。
容错服务:Agent可以存储在物理介质上,以防止意外。
安全服务:D’Agent用公钥系统来对Agent进行身份认证,用静态资源管理来管理Agent对资源的访问,不但可以授权访问某种资源,还可以限制使用。
通信服务:支持异步或同步通信模式。
生命周期服务:支持复制克隆和远程创建等操作。
2.Aglet
Aglet是由日本IBM公司用纯JAVA开发的移动Agent技术,并提供了实用的平台-----Aglet Workbench,让人们开发或执行移动Agent系统。到目前为止,Aglet是上述出现实例中最为成功和全面的系统之一,主要表现在以下几个方面:
(1)提供了一个简单而全面的移动Agent编程模型。
(2)为Agent间提供了动态而有效的通信机制。
(3)提供了一套详细且易用的安全机制。
Aglet这个词是由”Agent”和”applet”两个字合成的。简单地说,Aglet就是具有Agent行为的Java applet对象。但Aglet同时传送代码及其状态,而applet只传送代码。Aglet以线程的形式产生于一台机器上,可以随时暂停执行的工作,而后整个Agent可以被分派到另一个机器上,再重新启动执行任务。因为它占用的是线程,所以不会消耗太多的系统资源。
3.Concordia
Concordia系统也是基于JAVA语言开发的一个移动Agent系统,它由Agent Manager,Security Manager, Queue Manager,Event Manager和Service Bridge模块组成。在每个网络节点上,Concordia都在运行Java虚拟机上的服务器,负责Agent的移动,持久性,安全,通讯等工作。Concordia 注重系统的安全性和容错性,系统安全保护措施是双向的,既保护节点资源又保护移动Agent本身。Concordia 对Agent的权限限制主要由使用者决定,这是它和Aglet系统的一个区别。Concordia 的通讯利用现有的TCP/IP通讯服务,其通讯模块提供的是异步通讯模式,没有提供同步通讯模式,这是该系统的一个不足之处。
4.Voyager
Recursion公司的Voyager可以看成是一个增强了的对象请求代理(ORB),同其它移动Agent系统相比,Voyager与Java语言的结合更加紧密,既可用于开发移动Agent系统,也可用于创建传统的分布式系统。Voyager是一个纯Java分布式计算平台,可用来迅速生成高性能分布式应用程序,是代表当前技术水平的一个优秀的移动Agent开发平台。
5.Grasshoper
Grasshoper是目前第一个符合MASIF标准,也是目前唯一符合MASIF和FIPA标准的,基于JAVA的移动Agent系统。 Grasshoper环境包括代理处(Agency)和域注册器,通过专有的ORB(Grasshoper ORB)互联。Agency(相当于MASIF中的Place)是代理实际的运行环境,数个Agency结合构成域,还可以通过JAVA RMI和Socket连接进行通讯。整个通讯结构实现采用插件技术,具有很好的扩充性。在容错服务方面,系统崩溃之后可以从存储介质中恢复Agent的原来执行状态.安全身份证用X. X09证书实现,传输中的安全性由SSL来保证,访问控制通过用户定制的安全管理来完成,实现了数字鉴名。通讯服务支持异步或同步的通讯,动态消息机制和多点发送:生命周期支持复制远程创建和克隆等操作。
1.Aglet系统框架
Aglet的系统框架如图2.1所示:
图2.1 Aglet的系统框架
首先当一个正在执行的Aglet想要将自己迁移到远端时,会对Aglet Runtime层发出请求;接着Aglet Runtime层将Aglet的状态信息与代码转换成序列化的字节数组。这时如果成功,系统会将字节数组传送至ATCI(Agent Transport and Communication)接口,ATP是一个简单的应用层协议。接着,系统会将字节数组附上相关的系统信息,如系统名称以及Aglet的id等,并以比特流方式通过网络传至远程机器,远程机器通过ATCI层提供的ATP接口,接收到系统传来的字节数组及系统信息;最后,Aglet Runtime层对字节数组反序列化,得到Aglet的状态信息与代码,此时Aglet可以在远程机器上执行。
2.Aglet的生命周期
Aglet系统提供一个上下文环境(context)来管理Aglet的基本行为,包括创建(create)Agent、复制(clone)Agent、分派(dispatch)Agent到远端机器、召回(retract)远端的Agent、暂停(deactive)、唤醒(active)Agent,以及清除(dispose)Aglet等,其过程如图2.2所示:
图2.2 Aglet的生命周期
3.Aglet工作台及Aglet的包结构
Aglet工作台是一个可视化环境,它被用来建立使用移动Agent的网络应用。目前它提供的工具包括:
(1)移动Agent的框架。
(2)ATP-----Agent传输协议。
(3)Tazza-----可视化地开发应用所需的个性化的移动Agent。
(4)JDBC-----用于访问DB2数据库。
(5)JoDax------访问单位的数据。
(6)Tahiti-----可视化Agent的管理页面。
(7)Fiji-----通过在WEB页面上对Agent实行生命周期控制。
Aglet包含如下几个包:
com.ibm.Aglet:这个包定义Aglet的一些接口以及提供管理Agent上下文环境,以及信息的发送和接收的常用方法。
com.ibm.Aglet.event:此包实现了对象模型中的生命周期。
com.ibm.Aglet.system:主要是管理Aglet运行层的一些函数。
com.ibm.Aglet.util:提供一些公共类,如letAudioClip。
com.ibm.Aglet.patterns:包含常见的消息传递模式,如Master-Slave、Notifier-Notification等。
分布式计算实现矩阵乘法。
在局域网中两台机器,一台为Master,一台为Slave。Master创建Matrix类,并创建Calculator类,发送到Slave机器上运行。Master机器将Slave机上运行完成后的Agent回收(结果),再和本机上的运行结果合并,并显示出最终结果。
package examples.Matrix;
import com.ibm.aglet.*;
import com.ibm.aglet.event.*;
import com.ibm.aglet.util.*;
import java.lang.InterruptedException;
import java.io.Externalizable;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.IOException;
import java.net.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Enumeration;
/**作者:张华**/
public class Matrix extends Aglet {
transient MyDialog my_dialog = null; //没有串行化
URL dgp = null;//客户端地址
String message = null;
//矩阵初始化
int matone[][]=new int[10][10];
int mattwo[][]=new int[10][10];
int matthr[][]=new int[10][10];
boolean Flag;//设置标志量
int all = 0;
AgletProxy outer;//Agent客户机地址
//设置初始窗口
public void onCreation(Object init) {
Flag=true;
setMessage("Choose remote machine and GO!");//窗口显示信息
createGUI(); //创建窗口
}
//创建窗口
protected void createGUI(){
my_dialog = new MyDialog(this);
my_dialog.pack();
my_dialog.setSize(my_dialog.getPreferredSize());
my_dialog.setVisible(true);
}
//窗口显示信息
public void setMessage(String message){
this.message = message;
}
//处理信息
public boolean handleMessage(Message msg) {
if (msg.sameKind("Finish")) {
OutPut(msg);
} else if (msg.sameKind("startTrip")){
startTrip(msg);
} else{
return false;
};
return true;
}
//报告已经返回并销毁
public void OutPut(Message msg) {
setText("output begin");
while (Flag){
waitMessage(5*10);
};
my_dialog.msg.append("/n"+msg.getArg("answer").toString()+"/n");
}
public synchronized void startTrip(Message msg) { //到达远程机器
String destination = (String)msg.getArg(); // 得到远程机器地址
// 客户端得到 Agent 的内容
try{
outer=getAgletContext().createAglet(null,"examples.Matrix.Calculator",getProxy());
}catch(Exception e){setText("wrong!");};
try{
dgp=new URL(destination); //dgp 为客户端地址
}catch(MalformedURLException e){setText("wrong!");};
try{
outer.dispatch(dgp); // 发送到客户端
}catch(Exception e){setText("wrong!");};
matrixrun(); //本机上任务开始运行
}
//本机上程序
public void matrixrun(){
setText("matrixrun begin");
for (int x=0;x<10;x++){
for(int y=0;y<10;y++){
matone[x][y]=1;
mattwo[x][y]=1;
matthr[x][y]=0;
}
};
for (int x=0;x<5;x++){
for (int y=0;y<10;y++){
for (int z=0;z<10;z++){
matthr[x][y]=matthr[x][y]+matone[x][z]*mattwo[z][y];
}
}
};
Flag=false;
String result="";
for (int x=0;x<5;x++){
for (int y=0;y<10;y++){
result=result+"A["+x+"]["+y+"]="+matthr[x][y]+" ";
}
}
//打印本机结果
my_dialog.msg.append("The result shows down:"+"/n"+result+"/n"+"Local is over!");
setText("matrixrun over");
}
}
//MyDialog 窗口
class MyDialog extends Frame implements WindowListener, ActionListener{
private Matrix aglet = null;
//下面是一些GUI组件
private AddressChooser dest = new AddressChooser();
public TextArea msg =
new TextArea("",10,20,TextArea.SCROLLBARS_VERTICAL_ONLY);
private Button go = new Button("GO!");
private Button close = new Button("CLOSE");
//创建对话窗口
MyDialog(Matrix aglet) {
this.aglet = aglet;
layoutComponents();
addWindowListener(this);
go.addActionListener(this);
close.addActionListener(this);
}
//组件的布局
private void layoutComponents() {
msg.setText(aglet.message);
GridBagLayout grid = new GridBagLayout();
GridBagConstraints cns = new GridBagConstraints();
setLayout(grid);
cns.weightx = 0.5;
cns.ipadx = cns.ipady = 5;
cns.fill = GridBagConstraints.HORIZONTAL;
cns.insets = new Insets(5,5,5,5);
cns.weightx = 1.0;
cns.gridwidth = GridBagConstraints.REMAINDER;
grid.setConstraints(dest, cns);
add(dest);
cns.gridwidth = GridBagConstraints.REMAINDER;
cns.fill = GridBagConstraints.BOTH;
cns.weightx = 1.0;
cns.weighty = 1.0;
cns.gridheight = 2;
grid.setConstraints(msg, cns);
add(msg);
cns.weighty = 0.0;
cns.fill = GridBagConstraints.NONE;
cns.gridheight = 1;
Panel p = new Panel();
grid.setConstraints(p, cns);
add(p);
p.setLayout(new FlowLayout());
p.add(go);
p.add(close);
}
//事件的处理
public void actionPerformed(ActionEvent ae){
//对“go”按钮的处理
if("GO!".equals(ae.getActionCommand())){
aglet.setMessage(msg.getText());
msg.setText("");
try{
AgletProxy p = aglet.getProxy();
p.sendOnewayMessage(new Message("startTrip", dest.getAddress()));
}catch(Exception e) {
e.printStackTrace();
}
}
// 对“close”按钮的处理
else if("CLOSE".equals(ae.getActionCommand())){
setVisible(false);
}
}
public void windowClosing(WindowEvent we){
dispose();
}
public void windowOpened(WindowEvent we){
}
public void windowIconified(WindowEvent we){
}
public void windowDeiconified(WindowEvent we){
}
public void windowClosed(WindowEvent we){
}
public void windowActivated(WindowEvent we){
}
public void windowDeactivated(WindowEvent we){
}
}
//Calculator.java
package examples.Matrix;
import java.lang.InterruptedException;
import java.io.Externalizable;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.IOException;
import java.net.*;
import java.util.Enumeration;
import com.ibm.aglet.*;
/**作者:张华**/
public class Calculator extends Aglet{
AgletProxy proxy=null;
int matone[][]=new int[10][10];
int mattwo[][]=new int[10][10];
int matthr[][]=new int[10][10];
public void onCreation(Object init){
setText("creation begin");
try{
proxy=(AgletProxy)init;
}catch(Exception e){setText("wrong!1");};
}
public void run(){
setText("Calculator begin");
for (int x=0;x<10;x++){
for(int y=0;y<10;y++){
matone[x][y]=1;
mattwo[x][y]=1;
matthr[x][y]=0;
}
};
for (int x=0;x<5;x++){
for (int y=0;y<10;y++){
for (int z=0;z<10;z++){
matthr[x+5][y]=matthr[x+5][y]+matone[x+5][z]*mattwo[z][y];
}
}
};
String result="";
setText("sendonewaymsg begin");
//客户端上从5开始
for (int x=5;x<10;x++){
for (int y=0;y<10;y++){
result=result+"A["+x+"]["+y+"]="+matthr[x][y]+" ";
}
};
//将结果返回到主机
try{
Message msg=new Message("Finish");
msg.setArg("answer",result);
System.out.println(msg.getArg("answer"));
this.proxy.sendOnewayMessage(msg);
}catch(Exception e){setText("wrong!2");};
}
}
在实现中,我们采用了主从(master-slave)模式,这是aglet支持的众多模式中的一种,它充许主aglet把任务派发(dispatch)给从aglet,从aglet携带代码与状态移动到指定的目的主机上,与当地数据资源结合进行计算后,再将计算后的结果返回给主aglet 。
1.首先,通知主Agent向某个从Agent发送调用命令。
//主Agent的代理
AgletProxy p = aglet.getProxy();
//向主Agent发送消息,
p.sendOnewayMessage(
new Message("AgletClient1", “atp://localhost:82”));
Aglet与Aglet之间的通信,是使用消息传递的方式来传递消息对象的。此外,基于安全上的考虑,Aglet并非让外界直接存取其信息,而是透过一个代理(proxy)提供相应的接口与外界沟通。这样做有一个好处,即Aglet的所在位置会透明化,也就是说Aglet想要与远端的Aglet沟通时,只在本地主机的上下文环境中产生对应远端Aglet的代理,并直接与代理沟通即可,不必直接处理网络连接与通信的问题。
2.进入消息循环。
public boolean handleMessage(Message msg)
{
if (msg.sameKind("Finish"))
{
processRosponseResult(msg);
}
else if (msg.sameKind("AgletClient1"))
{
processAgletClient1(msg);
}
else
{
return false;
};
return true;
}
无论是主Agent还是从Agent都要继承自Agent类,并且要覆盖超类Agent类的消息循环处理函数handleMessage(),比如说现在收到的消息为AgletClient1,那么将要执行processAgletClient1(msg)方法。
3.在消息处理函数中,首先从环境(Context)中取得从Agent在本地的代理,然后将从Agent派发到目的主机上。
图3.1 消息处理方法
4.从Agent与当地主机上的数据资源结合经过计算,并通过消息返回计算结果。
图3.2 从Agent代码片断
1) 从网上下载Aglet框架。我下载的是:aglets-2.0.2.jar
2)解压: jar xvf aglets-2.0.2.jar ( 注意:不能安装在中文目录下 )
3) 执行ANT命令:
4)设置Java policy:
5)设置环境变量:
AGLETS_HOME= E:/java/aglets2.02
AGLETS_PATH=%AGLETS_HOME%
PATH=%PATH%;%AGLETS_HOME%/bin
6) 将编译后的代码放到 %AGLETS_HOME%/public 目录下去,如下图:
7)启动Aglet (默认用户名是anonymous, 默认密码是:aglets)
启动后的Agent平台如下图所示:
8)创建主Agent (也就是Matrix类),点击“Create”按钮后如下图:
在上图中的Aglet name处输入Matrix类的全路径名(包名加类名),然后点击Create按钮,如下图:
9)将从Aglet发送到Slave机器上去执行运行,并回收结果。
在Adress处输入从Aglet的地址:atp://localhost:4434
( 注意:因为我的是本机,所以写的是 localhost, 确保从 Agent 机器上开启 Aglet 服务 )
运行后的结果如下图所示:
10) OK
移动Agent的代码是可以在各个域机器上移动,并结合本地机器资源进行计算的。
1)采用移动Agent的master-slave结构可以实现集群的集中式管理。比如说,在集群主结点机器上部署一个应用之后(相当于Aglet中的主Agent), 完全可以由从Agent将应用转换成字节流之后再移动到集群各从节点机器中自动部署,这样,这可以让用户部署集群应用时只部署在一台机器上就行了,提高了用户体验。
2)如果集群主结点想要了解各从结点的各类信息(如应用本身的状态信息、JVM的堆信息、甚至操作系统的内存CPU利用率等信息)都可以通过发送从Agent到从结点机器上采集信息后再返回得到。
3)如果主结点想要控制从结点(如启、停等等),在定义好协议之后,都可以通过从Agent代码移动到从结点机器上执行协议规定的相关操作即可。
4)如果移动Agent与ALI命令行框架相结合,所有管理操作都将可以在命令行中完成。