返回列表 回复 发帖

[转帖]智能化分布式计算新模型(第二部分)

[这个贴子最后由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的一些有用资料。





                     我是一个呼吸着现在的空气而生活在过去的人
               这样的注定孤独,孤独的身处闹市却犹如置身于荒漠
                                     我已习惯了孤独,爱上孤独
                                 他让我看清了自我,还原了自我
                             让我再静静的沉思中得到快乐和满足
                                   再孤独的世界里我一遍又一遍
                                   不厌其烦的改写着自己的过去
                                             延伸到现在与未来
                                       然而那只是泡沫般的美梦
                                 产生的时刻又伴随着破灭的到来
                         在灰飞烟灭的瞬间我看到的是过程的美丽
                                      而不是结果的悲哀。。。

[转帖]智能化分布式计算新模型(第二部分)

***** 版主模式 *****
关于agent
<a href=topic.cgi?forum=22&topic=5>该贴子已被管理员转移,请点击这里查看</a>

                     我是一个呼吸着现在的空气而生活在过去的人
               这样的注定孤独,孤独的身处闹市却犹如置身于荒漠
                                     我已习惯了孤独,爱上孤独
                                 他让我看清了自我,还原了自我
                             让我再静静的沉思中得到快乐和满足
                                   再孤独的世界里我一遍又一遍
                                   不厌其烦的改写着自己的过去
                                             延伸到现在与未来
                                       然而那只是泡沫般的美梦
                                 产生的时刻又伴随着破灭的到来
                         在灰飞烟灭的瞬间我看到的是过程的美丽
                                      而不是结果的悲哀。。。
返回列表