  
- UID
- 1
- 威望
- 1240 点
- 金钱
- 24019 金币
- 点卡
- 317 点
|
[转帖]智能化分布式计算新模型(第二部分)
[这个贴子最后由cnangel在 2003/06/29 07:27pm 第 1 次编辑]
肖文鹏 (xiaowp@263.net)
北京理工大学的硕士研究生
2003 年 6 月
在本系统文章的第一部分中,你已经大致了解了移动Agent这一新的分布式计算新模型,目前移动Agent技术已经走出了实验室,开始真正进入实际应用。在这一部分中,你将掌握如何在Voyager平台上开发移动Agent应用程序。
一、典型移动Agent系统
移动Agent目前已经从理论探索进入到实用阶段,涌现出了一系列较为成熟的开发平台和执行环境。理论上移动Agent可以用任何语言编写(如C/C++、Java、Perl、Tcl和Python等),并可在任何机器上运行,但考虑到移动Agent本身需要对不同的软硬件环境进行支持,所以最好还是选择在一个解释性的、独立于具体语言的平台上开发移动Agent。Java是目前开发移动Agent的一门理想语言,因为经过编译后的Java二进制代码可以在任何具有Java解释器的系统上运行,具有很好的跨平台特性。
移动Agent技术虽然已经研究了很多年,但直到1996年才出现了真正实用的移动Agent系统,目前使用的移动Agent系统大致可以分为三类:一类是基于传统解释语言的,一类是基于Java语言的,另一类则是基于CORBA平台的。下面介绍几个典型的移动Agent系统,它们代表了当今移动Agent技术的基本方向和潮流:
General Magic公司的Odysses
作为移动Agent系统专用语言的最早尝试,General Magic公司开发的Telescript曾经在过去的几年里被广泛采用。Telescript是一种面向对象的解释性语言,用它编写的移动Agent在通信时可以采用两种方式:若在同一场所运行,Agent间可以相互调用彼此的方法;若在不同场所运行,Agent间需要建立连接,互相传递可计算的移动对象。Telescript在开始出现时还是一个比较成功的移动Agent开发平台,其安全性和健壮性都比较好,执行效率也很高,Telescriipt中的三个基本概念(agent、place和go)对移动Agent做了一个很精辟的阐述:代理自主移动(agent go place)。
随着Java的迅速崛起及其跨平台特性的逐步完善,Telescript的优势慢慢消失,General Magic公司开始改变其策略,开发了一个完全用Java实现的移动Agent系统Odyssey,它能够支持Java RMI,Microsoft DCOM,以及CORBA IIOP。Odyssey继承了Telescript中的许多特性,是目前被广泛使用的一个移动Agent开发平台。
IBM公司的Aglet
Aglet是最早基于Java的移动Agent开发平台之一,Aglet的名字来源于Agent和Applet,可以简单地将其看成具有Agent行为的Applet对象。Aglet以线程的形式产生于一台机器,需要时可以随时暂停正在执行的工作,并将整个Aglet分派到另一台机器上,然后继续执行尚未完成的任务。从概念上讲,一个Aglet就是一个移动Java对象,它支持自动运行的思想,可以从一个基于Aglet的主机移动到其它支持Aglet的主机上。
Aglet构造了一个简单而全面的移动Agent编程框架,为移动Agent之间的通信提供了动态而有效的交互机制,同时还具备一整套详细而易用的安全机制,这一切使得移动Agent的开发变得相对简单起来。
Recursion公司的Voyager
Voyager可以看成是一个增强了的对象请求代理(ORB),同其它移动Agent系统相比,Voyager与Java语言的结合更加紧密,既可用于开发移动Agent系统,也可用于创建传统的分布式系统。Voyager是一个纯Java分布式计算平台,可用来迅速生成高性能分布式应用程序,是代表当前技术水平的一个优秀的移动Agent开发平台。
二、Voyager移动代理编程
Java是目前开发移动Agent的最佳语言,Recursion公司开发的Voyager是一个高效的移动Agent支持平台,可以用来迅速、便捷地开发基于移动Agent的高性能分布式应用程序,下面辅以具体的实例介绍如何在Voyager平台上利用Java语言来开发移动Agent,所有的例子均在Voyager 4.6和JDK 1.4.1下调试通过,操作系统使用的是Red Hat Linux 9。
2.1 Voyager的启动和终止
安装好Voyager软件包后,可以从命令行方式启动Voyager服务器,接受来自Voyager程序的移动Agent对象和消息。例如,要在端口8000上启动Voyager服务器,可以使用如下命令:
[gary@gary gary]$ voyager 8000
如果一切正常,将显示如下信息:
voyager orb professional 4.6, copyright recursion software 1997-2003
在命令行方式下终止Voyager服务器,可以按Ctrl+C键。
启动和终止Voyager的另一个方法是调用Voyager提供的API。要在Java程序中启动Voyager,可以调用Voyager.startup()函数,以下是启动Voyager的一些常用方法:
Voyager.startup() // 启动Voyager
Voyager.startup(8000) // 在端口8000上启动Voyager
Voyager.startup("//gary:8000")// 在主机gary的8000端口启动Voyager
在Java程序中终止Voyager的方法是调用Voyager.shutdown()函数。
2.2 Voyager应用程序框架
在借助Voyager平台开发移动Agent应用程序时,通常需要遵循以下几个基本步骤:
定义远程接口
实现远程接口
开发使用远程接口的客户程序
启动Voyager服务器
运行Voyager客户机
假设要开发一个很简单的Voyager服务程序,用于实现两个整数的相加,Voyager客户通过派遣一个Agent到服务端,完成对所携带的两个整数的相加,并将计算结果由移动Agent带回到客户机。下面借助Voyager提供的开发平台来实现这一移动Agent应用程序,从中不难得出Voyager应用程序的基本框架。
步骤一、定义远程接口
Voyager的远程接口与Java的RMI类似,由一系列声明构成。Java语言提供的接口机制使分布式计算变得可能,许多分布式计算模型(包括移动Agent)都利用接口来简化分布式应用程序开发的复杂度。Voyager中的远程对象由移动Agent来表征,正是由于Agent具有与远程对象相同的接口,才使得一个接口类型的变量有可能通过Agent来引用远程对象。
Voyager中的接口名一般以字母I开头,建议在使用Voyager开发移动Agent应用程序时尽量遵循这一规则。在本例中用到的远程接口名为ICalculator,该接口中只定义了一个名为add的方法,其功能是完成两个整数的相加,并返回计算结果,如例1所示:
例1. ICalculator.java
/*
* @(#) ICalculator.java
*/
// 远程计算接口
public interface ICalculator
{
int add(int a, int b);
}
步骤二、实现远程接口
使用Voyager开发移动Agent应用程序的第二步是实现上面定义的远程接口,在本例中就是要编写一个实现了ICalculator接口的类Calculator,如例2所示:
例2. Calculator.java
/*
* @(#) Calculator.java
*/
import java.io.*;
public class Calculator implements ICalculator, Serializable
{
public Calculator() {
System.out.println("Constuct calculator object.");
}
// 远程计算接口的实现
public int add(int a, int b) {
int result;
result = a + b;
return result;
}
}
除了实现ICalculator接口外,Calculator类还实现了Serializable接口,这是因为Voyager同RMI一样要实现对象的迁移,需要借助Java语言中对象的序列化机制。
步骤三、开发客户机
在定义并实现远程接口之后,接下去要做的就是开发使用远程接口的Voyager客户程序。例3是一个简单的Voyager客户机实现代码:
例3. CalcClient.java
/*
* @(#) CalcClient.java
*/
import com.objectspace.voyager.*;
public class CalcClient
{
public static void main(String args[]) {
ICalculator calcObj;
int a = 1, b = 3;
int result;
try {
// 启动Voyager
Voyager.startup();
// 创建远程计算对象
calcObj =
(ICalculator) Factory.create("Calculator", "//gary:8000");
// 向远程对象发送消息
result = calcObj.add(a, b);
System.out.println(a + " + " + b + " = " + result);
// 关闭Voyager
Voyager.shutdown();
} catch(Exception exception) {
System.err.println( exception );
}
}
}
客户端的代码比较简单,值得注意的是如何在Java程序中启动Voyager作为客户机,以及如何使用Factory.create()来创建远程对象。
通过调用Factory类的create()方法,可以在特定位置上创建对象,该方法返回新建对象的远程代理,如果该代理目前还不存在则被自动创建。create()方法有多种形式,分别用来创建本地对象和远程对象。例如,要在本地程序中创建一个名为Calculator类的实例,可以调用如下的代码:
ICalculator calcObj = (ICalculator) Factory.create("Calculator");
与之对应,如果想在一个远端Voyager程序中创建一个名为Calculator类的实例,假设该程序运行在主机gary的8000端口上,则可以调用如下的代码:
ICalculator calcObj =
(ICalculator) Factory.create("Calculator", "//gary:8000");
步骤四、启动Voyager服务器
要对开发的Voyager应用程序进行测试,首先需要在服务器端以命令行方式启动Voyager,根据本例中客户程序的具体要求,监听端口应该设为8000:
[gary@gary gary]$ voyager 8000
步骤五、运行Voyager客户机
一切准备就绪,现在可以在客户端执行下面的命令来启动Voyager客户机程序了:
[maggie@maggie maggie]$ java CalcClient
如果客户机运行正常,Calculator构造函数中的输出信息将显示在Voyager服务器上,而计算结果则将显示在Voyager客户机上。
2.3 Voyager对象的移动性
可移动性是移动Agent区别于其它软件Agent的本质特征,在Voyager中移动对象到新的位置非常简单,只需要遵循下面两个步骤:
调用Mobility.of()方法得到可移动对象;
调用IMobility接口中的moveTo()方法移动对象;
在调用moveTo()方法移动Voyager对象时,可移动对象将按以下步骤顺序执行任务:
对象中正在处理的消息将完成,但新发送给对象的所有消息都将暂停执行;
对象及其状态都将使用Java的序列化机制复制到新位置,如果无法完成序列化或者网络出现故障,则抛出异常;
对象的新地址在原位置处被存储,存储的新地址不会被垃圾回收器当成对象引用;
旧对象被销毁;
在旧对象上挂起的消息重新执行;
当消息通过代理传送给对象的旧地址时,将向代理抛出一个包含对象新地址的异常。代理捕获该异常后绑定新的对象地址,重新向对象的新地址发送消息;
如果对象移动成功,moveTo()方法将返回;如果对象移动失败,则异常将被抛出,同时对象恢复到原状态;
下面以一个简单的问候程序来演示在Voyager中移动对象,该程序将创建一个对象,然后移动到其它机器上输出问候信息。按照Voyager应用程序基本框架的要求,首先定义远程接口IMessage,如例4所示:
例4. IMessage.java
/*
* @(#) IMessage.java
*/
// 远程接口
public interface IMessage
{
void showMessage(String msg);
}
接着实现IMessage接口,如例5所示:
例5. Message.java
/*
* @(#) Message.java
*/
import java.io.*;
// 远程接口实现
public class Message implements IMessage, Serializable
{
public void showMessage(String msg) {
System.out.println(msg);
}
}
同样,Message类除了实现IMessage接口外,还实现了Serializable接口,这是因为对象移动同样需要用到Java的序列化机制。Voyager中的每个可移动对象都必须实现Serializable接口,否则在移动过程中将产生异常。
接下去就能够编写相应的Voyager客户机代码来实现对象的移动了,如例6所示:
例6. Action.java
/*
* @(#) Action.java
*/
import com.objectspace.voyager.*;
import com.objectspace.voyager.mobility.*;
public class Action
{
public static void main(String argv[]) {
try {
// 在9000端口启动Voyager
Voyager.startup("9000");
IMessage message = (IMessage) Factory.create("Message");
IMobility mob = Mobility.of(message);
// 移动Agent到远程计算机
mob.moveTo("//gary:7000");
message.showMessage("Hello World!");
// 关闭Voyager
Voyager.shutdown();
} catch (Exception e) {
System.err.println(e);
}
}
}
现在可以对Voyager对象的移动性进行测试了,首先在主机gary的7000端口上启动Voyager服务器:
[gary@gary gary]$ voyager 7000
然后在另一台主机上运行Voyager客户机:
[maggie@maggie maggie]$ java Action
Action类首先在本机的9000端口上启动Voyager,然后调用Factory.create()方法创建名为Message的类实例,接着用Mobility.of()方法将其转变成可移动对象,并调用moveTo()方法将其移动到主机gary的 7000端口上去。
对象成功移动之后,整个运行环境将随之发生变化,输出信息此时自然也就显示在远程主机上。在Voyager中,对象的移动对客户机来讲是透明的,但客户机对移动对象的引用始终都是有效的,无论该对象是位于本地主机、远程主机或者正处于移动过程之中。
2.4 Voyager移动Agent 开发
使用Voyager可以很容易地开发移动Agent应用程序,Voyaer中的移动代理由一个或多个可移动对象组成,在Voyager中开发移动Agent需要遵循下面两个步骤:
调用Agent.of()方法将Voyager对象转变成移动Agent;
调用IAgent接口中定义的方法实施移动Agent行为;
在利用Voyager开发移动Agent应用时,经常需要用到IAgent接口中定义的如下方法:
moveTo() 将Agent移动到指定位置。
setAutonomous() 设置移动Agent的自主状态,当移动Agent处于自主状态时,即使此时没有任何指向移动Agent的引用存在,垃圾收集程序也不会将其收回。移动Agent默认的自主状态为true,当移动Agent完成任务并希望被收回,可以调用setAutonomous(false)。
isAutonomous() 返回移动Agent当前的自主状态。
getHome() 返回移动代理当前所处位置的URL。
下面通过一个简单的例子来演示如何使用Voyager开发移动Agent应用程序,服务器端仍使用两整数相加的程序,同时开发一个加法器移动Agent,它可以自主移动到远程服务器上执行相加操作。首先依然是定义两个整数相加的远程接口ICalculator,如例7所示:
例7. ICalculator.java
/*
* @(#) ICalculator.java
*/
// 远程接口
public interface ICalculator
{
int add(int a, int b);
}
ICalculator接口中只有一个名为add的方法,该接口的具体实现Calculator如例8所示:
例8. Calculator.java
/*
* @(#) Calculator.java
*/
import java.io.*;
public class Calculator implements ICalculator, Serializable
{
public Calculator() {
System.out.println("Constuct calculator object.");
}
// 实现远程计算接口
public int add(int a, int b) {
int result;
result = a + b;
return result;
}
}
现在需要一个能移动到远程计算机上完成计算过程的移动Agent,先来编写相应的接口IAdder,如例9所示:
例9. IAdder.java
/*
* @(#) IAdder.java
*/
// 移动Agent的接口
public interface IAdder
{
void setOP1(int op);
void setOP2(int op);
void work(ICalculator calc);
void atMachine(ICalculator calc);
}
接口IAdder中定义了名为work和atMachine的两个方法,它们均使用ICalculator接口作为参数,例10是IAdder接口的具体实现:
例10. Adder.java
/*
* @(#) Adder.java
*/
import java.io.*;
import com.objectspace.voyager.*;
import com.objectspace.voyager.agent.*;
// 移动Agent接口的实现
public class Adder implements IAdder, Serializable
{
private int op1;
private int op2;
private int result;
public Adder() {
System.out.println("Construct an adder.");
}
public void finalize() {
System.out.println("Finalize adder.");
}
// 设置第一个操作数
public void setOP1(int op) {
op1 = op;
}
// 设置第二个操作数
public void setOP2(int op) {
op2 = op;
}
// 实现Agent的移动
public void work(ICalculator calc) {
try {
Agent.of(this).moveTo(calc, "atMachine");
} catch (Exception e) {
System.err.println(e);
}
}
// 完成移动Agent的计算
public void atMachine(ICalculator calc) {
// 输出Agent来自的主机名
System.out.println("At remote machine, home="
+ Agent.of(this).getHome());
System.out.println("Remote compute: "
+ op1 + " + " + op2 + " = " + compute(calc));
// 设置自主状态
Agent.of(this).setAutonomous(false);
}
// 计算方法
public int compute(ICalculator calc) {
return calc.add(op1, op2);
}
}
因为要使用Java的序列化机制来实现对象的移动,因此Adder除了实现IAdder接口外,还实现了Serializable接口。Adder类的核心是work()方法,在与远程主机上的Voyager服务程序建立起通信后,Addre类将移动到远端服务器上。
当移动Agent成功到达Voyager服务器后,atMachine()方法将在远端服务器上被执行,完成用户指派的计算任务。移动Agent在完成任务后调用setAutonomous()方法对自主状态进行设置,从而使得Adder对象可以被垃圾收集程序收回。
要测试移动代理Adder,需要编写一个主程序来生成本地和远程主机上的对象,如例11所示:
例11. MyAgent.java
/*
* @(#) MyAgent.java
*/
import java.awt.event.*;
import javax.swing.*;
import java.awt.*;
import com.objectspace.voyager.*;
// 移动Agent的用户界面
public class MyAgent extends JFrame implements ActionListener
{
private ICalculator calc;
private IAdder adder;
private JButton btnStartup = new JButton("Startup");
private JButton btnCompute = new JButton("Compute");
private JButton btnShutdown = new JButton("Shutdown");
private JTextArea edtStatus = new JTextArea(10, 20);
private JTextField edtOP1 = new JTextField(10);
private JLabel lblPlus = new JLabel(" + ");
private JTextField edtOP2 = new JTextField(10);
public MyAgent() {
super("Mobile Agent demo");
JPanel pane = new JPanel();
JPanel opane = new JPanel();
GridBagConstraints constraints = new GridBagConstraints();
GridBagLayout layout = new GridBagLayout();
JScrollPane scroll =
new JScrollPane(edtStatus,
ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
// 按钮的事件响应函数
btnStartup.addActionListener(this);
btnCompute.addActionListener(this);
btnShutdown.addActionListener(this);
// 界面布局
pane.setLayout(layout);
// 输入框
opane = new JPanel();
opane.add(edtOP1);
opane.add(lblPlus);
opane.add(edtOP2);
buildConstraints(constraints, 0, 0, 1, 1, 100, 100);
pane.add(opane);
layout.setConstraints(opane, constraints);
// 状态区域
buildConstraints(constraints, 0, 1, 1, 5, 100, 100);
edtStatus.setEditable(false);
edtStatus.append("Agent Status:\n");
pane.add(scroll);
layout.setConstraints(scroll, constraints);
// 开始按钮
buildConstraints(constraints, 1, 1, 1, 1, 100, 100);
layout.setConstraints(btnStartup, constraints);
pane.add(btnStartup);
// 计算按钮
buildConstraints(constraints, 1, 2, 1, 1, 100, 100);
layout.setConstraints(btnCompute, constraints);
btnCompute.setEnabled(false);
pane.add(btnCompute);
// 关闭按钮
buildConstraints(constraints, 1, 3, 1, 1, 100, 100);
layout.setConstraints(btnShutdown, constraints);
btnShutdown.setEnabled(false);
pane.add(btnShutdown);
setContentPane(pane);
pack();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
public void actionPerformed(ActionEvent evt) {
Object source = evt.getSource();
if (source == btnStartup) {
try {
// 启动Voyager
Voyager.startup();
edtStatus.append("\nStartup Voyager OK!\n");
btnStartup.setEnabled(false);
btnCompute.setEnabled(true);
btnShutdown.setEnabled(true);
} catch (Exception e) {
edtStatus.append("\nStartup Voyager FAIL!\n");
edtStatus.append(e.toString());
}
} else if (source == btnCompute) {
try {
int op1 = Integer.parseInt(edtOP1.getText());
int op2 = Integer.parseInt(edtOP2.getText());
// 创建远程计算对象
calc = (ICalculator) Factory.create("Calculator",
"//gary:7000");
// 创建可移动计算对象
adder = (IAdder)Factory.create("Adder");
adder.setOP1(op1);
adder.setOP2(op2);
// 实现Agent的移动,完成计算
adder.work(calc);
edtStatus.append("\nRemote Compute OK!\n");
} catch (Exception e) {
edtStatus.append("\nRemote Compute FAIL!\n");
edtStatus.append(e.toString());
}
} else if (source == btnShutdown) {
try {
关闭Voyager
Voyager.shutdown();
edtStatus.append("\nShutdown Voyager OK!\n");
btnStartup.setEnabled(true);
btnCompute.setEnabled(false);
btnShutdown.setEnabled(false);
} catch (Exception e) {
edtStatus.append("\nShutdown Voyager FAIL!\n");
edtStatus.append(e.toString());
}
}
}
public void buildConstraints(GridBagConstraints gbc, int gx, int gy,
int gw, int gh, int wx, int wy) {
gbc.gridx = gx;
gbc.gridy = gy;
gbc.gridwidth = gw;
gbc.gridheight = gh;
gbc.weightx = wx;
gbc.weighty = wy;
gbc.fill = GridBagConstraints.BOTH;
}
public static void main(String argv[]) {
JFrame frame = new MyAgent();
}
}
MyAgent类在本地主机上启动Voyager后,在名为gary的主机上运行的Voyager服务器中创建Calculator类,并在本地主机创建Adder类,最后调用Adder类中的work()方法来完成计算任务。
要运行这个例子,首先在主机gary上执行下面的命令启动Voyager服务器:
[gary@gary gary]$ voyager 7000
接着在另一台计算上执行如下命令运行Voyager客户机:
[maggie@maggie maggie]$ java MyAgent
MyAgent运行时的界面如图6所示:
<img src="http://www-900.ibm.com/developerWorks/cn/java/l-mobile-agent/image202.jpg" width=387 height=240 border=0>
在运行MyAgent时,首先单击Startup按钮启动Voyager,接着在窗口顶部的输入框中填入参与计算的两个整数后,单击Compute按钮就可以将一个移动Agent派遣出去,如果要关闭Voyager,可以单击Shutdown按钮。
在上面的例子中,Adder类是一个移动Agent,它通过网络移动到主机gary上完成最终的计算任务,计算结果将在远端服务器上显示出来。
三、小结
近年来,移动Agent技术无论在理论研究还是在实际应用中都取得了很大的进步,人们开始对Agent有了一个较为清晰的认识,并随之产生了一些成熟的移动Agent开发平台,Voyager就是其中的一个典型代表,它的出现极大地推动了移动Agent的应用范围。21世纪是人类社会全面实现信息化的时代,随着计算机网络和人工智能技术的进一步发展,移动Agent必将揭开信息革命的新篇章。
参考资料
在Recursion公司的网站上可以找到与Voyager相关的一些资料,以及最新的Voyager软件包,http://www.recursionsw.com。
Aglet是IBM公司提供的一个移动Agent开发框架,完全基于Java语言实现,并且开放源代码,在网站http://www.aglet.org上可以找到Aglet的一些有用资料。
|
我是一个呼吸着现在的空气而生活在过去的人
这样的注定孤独,孤独的身处闹市却犹如置身于荒漠
我已习惯了孤独,爱上孤独
他让我看清了自我,还原了自我
让我再静静的沉思中得到快乐和满足
再孤独的世界里我一遍又一遍
不厌其烦的改写着自己的过去
延伸到现在与未来
然而那只是泡沫般的美梦
产生的时刻又伴随着破灭的到来
在灰飞烟灭的瞬间我看到的是过程的美丽
而不是结果的悲哀。。。
|
|