mobile agent 学习

来源:互联网 发布:华为 中兴 知乎 编辑:程序博客网 时间:2024/06/05 08:52

一、典型移动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()         // 启动VoyagerVoyager.startup(8000)        // 在端口8000上启动VoyagerVoyager.startup("//gary:8000")// 在主机gary的8000端口启动Voyager

在Java程序中终止Voyager的方法是调用Voyager.shutdown()函数。

2.2 Voyager应用程序框架

在借助Voyager平台开发移动Agent应用程序时,通常需要遵循以下几个基本步骤:

  1. 定义远程接口
  2. 实现远程接口
  3. 开发使用远程接口的客户程序
  4. 启动Voyager服务器
  5. 运行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中移动对象到新的位置非常简单,只需要遵循下面两个步骤:

  1. 调用Mobility.of()方法得到可移动对象;
  2. 调用IMobility接口中的moveTo()方法移动对象;

在调用moveTo()方法移动Voyager对象时,可移动对象将按以下步骤顺序执行任务:

  1. 对象中正在处理的消息将完成,但新发送给对象的所有消息都将暂停执行;
  2. 对象及其状态都将使用Java的序列化机制复制到新位置,如果无法完成序列化或者网络出现故障,则抛出异常;
  3. 对象的新地址在原位置处被存储,存储的新地址不会被垃圾回收器当成对象引用;
  4. 旧对象被销毁;
  5. 在旧对象上挂起的消息重新执行;
  6. 当消息通过代理传送给对象的旧地址时,将向代理抛出一个包含对象新地址的异常。代理捕获该异常后绑定新的对象地址,重新向对象的新地址发送消息;
  7. 如果对象移动成功,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需要遵循下面两个步骤:

  1. 调用Agent.of()方法将Voyager对象转变成移动Agent;
  2. 调用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 MyAgentMyAgent运行时的界面如图6所示:


图6. MyAgent用户界面

在运行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的一些有用资料。 
原创粉丝点击