本文是分布式智能软件课程的笔记及心得记录
大纲
JADE发展史
1998年年底意大利通信公司Telecom发起第一个软件开发计划,最终发展成了JADE(Java Agent DEvelopment framework)平台
JADE平台的主要优势是遵循FIPA规范,其基于JAVA语言实现了一个Agent抽象并提供友好的API
JADE中的Agent抽象思想
- Agent具有自主性:Agent不向其他Agent提供回调功能和自身对象引用,因此Agent不能控制其他Agent服务
- Agent具有主动性:Agent具有自己的线程,利用其控制自己的生命周期和自主决定的动作
- Agent之间是松耦合的:Agent之间的通信是异步的,发送者和接受者之间没有时间依赖,发送者不需要知道接受者的准确名称标识,只需知道接受者是一个具有某种特征的Agent集合
- Agent是端到端的:每个Agent都定义了一个全局唯一名称标识,可以任何时刻加入或者离开JADE平台,并通过白页和黄页服务发现其他Agent
JADE核心功能
基于上述Agent抽象思想,JADE为程序员提供一系列核心功能
- Agent分布式系统:每个Agent可以作为一个单独线程运行于不同远程机器相互透明交互
- 完全兼容FIPA规范
- 位置透明的消息传输机制:实现了高效的跨平台异步消息传递
- 白页黄页服务
- Agent生命周期管理:创建Agent时分配标识符和传输地址(白页服务使用),简单API和GUI界面完成本地远程Agent生命周期管理(创建、暂停、恢复、冻结、解冻、迁移、复制和销毁)
- Agent在进程、机器之间迁移服务
- Agent订阅机制
- GUI工具:监视Agent运行、单步调试等
- 本体和内容语言
- 交互协议库
JADE平台体系结构
主容器:Agent生存在容器中,容器是提供JADE运行环境、管理Agent运行所需服务的JAVA进程,主容器是平台的入口点,其他容器必须通过在主容器注册才能加入JADE平台
主容器特殊能力:
- 对容器表(CT)管理:CT中有平台所有容器节点对象引用和传输地址的注册表
- 对全局Agent描述表(GADT)管理:GADT中有平台所有Agent的注册表(Agent状态、位置等)
- 启动和管理特殊Agent——Agent管理系统(AMS):AMS是监督平台运行、提供白页服务的Agent,所有Agent都必须向AMS注册获得有效的AID
- 启动和管理特殊Agent——目录服务器(DF):DF是提供黄页服务、订阅服务的Agent,Agent需向DF注册其可提供的服务和查询DF来检索其他Agent服务
JADE平台安装及使用
官方网站下载JADE压缩包,如JADE-all-4.3.1.zip
解压后可得到四个文件
设置环境变量(JADE_HOME和ClassPath):根据解压路径,JADE_HOME为安装路径\JADE-bin-4.3.1\jade,ClassPath添加%JADE_HOME\lib\jade.jar%;D:\JADE\test
启动JADE平台:在命令行输入java jade.Boot -gui
jade.Boot是平台的主类,-gui表示启动平台的同时创建一个远程监视Agent RAM(Remote Monitoring Agent)可以图形化显示所有相关Agent与容器
HelloWorld类
编写HelloWorldAgent的源代码1
2
3
4
5
6
7
8
9
10
11
12//导入Agent包
import jade.core.Agent;
public class HelloWorldAgent extends Agent {
//每个启动的Agent会首先调用其setup()方法启动
protected void setup() {
//输出
System.out.println("Hello World! My name is "+getLocalName());
//调用doDelete()结束Agent生命周期
doDelete();
}
}
编译HelloWorldAgent代码
在HelloWorldAgent所在文件夹启动CLI或者GitBash,输入命令javac -encoding UTF-8 HelloWorldAgent.java
之后可在文件夹看到编译后的.class文件
启动JADE平台,点击主容器Main-Container右键Start New Agent添加HelloWorldAgent
AgentName为实例Agent名称,ClassName填入编译Agent类名称
点击OK可以看到在主容器下并没有出现新的Agent是因为HelloWorldAgent在输出信息后调用了doDelete()方法结束了自身,在控制台可看到其输出的消息
基础Agent类
创建Agent类:只需要从jade.core.Agent类继承Agent类
Agent类不同于java类:其对象由JADE平台实例化,对象名从不在外部公开,Agent间采用异步通信,不通过方法调用进行交互
setup()方法:用于实现Agent类的初始化操作,通常包括
- 显示图形用户界面
- 建立数据库连接
- 在黄页中注册其提供的服务
- 获取启动参数
- 添加初始行为
识别Agent:通过Agent标识符AID识别,可在Agent类内部通过getAID()方法查询自身标识符
AID:ADI类提供方法查询Agent实例的本地名称(getLocalName())、GUID(getName())、地址(getAllAddresses())
GetName类
编写GetName类1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16import jade.core.Agent;
import java.util.Iterator;
public class GetName extends Agent {
protected void setup() {
System.out.println("Hello World. I'm an agent!");
System.out.println("My local-name is "+getAID().getLocalName());
System.out.println("My GUID is "+getAID().getName());
System.out.println("My addresses are:");
Iterator it = getAID().getAllAddresses();
while (it.hasNext()) {
System.out.println("- "+it.next());
}
}
}
编译并运行
基础Agent类生命周期
终止Agent:可调用其doDelete()方法,Agent终止前会调用其takeDown()方法执行各种终止前的清理操作
带参数创建Agent:可通过指定String参数创建Agent,在Agent内的setup()方法中通过getArguments()以数组的形式获取启动参数1
2
3
4
5Object[] args = getArguments();
if (args != null) {
for (int i = 0; i < args.length; ++i)
System.out.println("- "+args[i]);
}
Agent行为
Agent行为表示Agent要执行的任务,通过继承来自jade.core.behaviours包中的Behaviour类来实现
添加行为:通过调用addBehaviour()方法将行为添加到Agent行为队列中
行为类基本方法:任何继承Behaviour的子类都必须重写两个抽象方法
- action()方法:行为类每次被调度时执行的操作
- done()方法:行为类结束操作后执行,返回一个boolean型变量,若为true则将行为从Agent队列删除,否则行为轮换到Agent队列后
Agent行为
- 普通行为Behaviour:包含最基本的action()和done()方法,还有一些行为的生命周期方法onStart(首次调用action()方法之前执行一次)/onEnd(调用done()方法返回true后执行一次)/block()/restart()
- 简单行为(Simple Behaviour)
- 单次行为OneShotBehaviour:action()方法仅执行一次,因为done()方法总是返回true,因此仅编写action()方法即可
- 循环行为CyclicBehaviour:action()方法不断被重复执行,因为done()方法总是返回false,仅编写action()方法即可
- 一个特殊的单次行为WakerBehaviour(唤醒行为)需要在构造行为时通过参数指定时间,行为开始后到达指定时间后执行onWake()方法,执行一次后自动结束行为
- 一个特殊的循环行为TickerBehaviour(周期行为):需要在构造行为时通过参数指定周期,周期性(隔一段时间)执行一次onTick()方法,可通过移除行为、调用stop()方法结束行为
- 每个Agent行为都拥有一个myAgent成员变量表示执行行为的Agent,提供了一种行为访问Agent资源的方法
- 每个调用行为的Agent都拥有一个removeBeahaviour()用于终止Agent行为队列中当前被调度执行的行为
- JADE Behaviour的依赖类图:
Agent通信
Agent通信模式:基于异步消息传递,每个Agent有一个Agent消息队列用于接收消息**
消息格式:遵循FIPA-ACL消息结构,包含如下字段
- 消息发送者
- 接受者列表
- 通信行为(通信原语),表示发送者希望通过发送消息达到的目的,如REQUEST表达请求执行特定动作,INFORM表示接收通知,CFP(Call for Proposal)表示发起一个协商
- 通信内容
- 内容语言,规定描述内容的语法
- 本体,规定内容中使用的符号词汇表
- 控制对话,如conversation-id,reply-with,in-reply-to字段等,接收回应超时设定reply-by字段等
消息实例:消息均是作为jade.lang.acl.ACLMessage的对象实例,其中包含一些方法
- 各种getter()/setter()来访问/设置消息的字段
- createReply()方法创建新的回复的ACLMessage对象
发送消息:
- 1.通过通信原语创建消息:ACLMessage msg = new ACLMessage(ACLMessage.INFORM);
- 2.设置收件Agent:msg.addReceiver(new AID(“Peter”,AID.ISLOCALNAME));
- 3.设置内容:msg.setContent(“Today it’s raining”);
- 4.发送:send(msg);
接收消息:
- 1.从消息队列中取出消息:ACLMessage msg=receive();
- 2.判断消息队列是否有消息:if(msg!=null){ // process the message};
消息模板:通过对jade.lang.acl.MessageTemplate实例化对象设置模板,使用模板可仅接收和模板匹配的消息,类似消息过滤器,模板中的静态匹配方法包括匹配通信行为(MatchPerformative(performative))/发送者(MatchSender(AID))/会话ID(MatchConversationID(String))/本体(MatchOntolog(String))等
- 1.创建消息模板:
- 创建匹配一种特性的模板,如MessageTemplate mt = MessageTemplate.MatchPerformative(ACLMessage.CFP);
- 创建匹配多种特性的模板,使用and、or、not逻辑运算符,如MessageTemplate mt = MessageTemplate.and(MessageTemplate.MatchPerformative(ACLMessage.INFORM),MessageTemplate.MatchSender(new AID(“Agent1”,AID.ISLOCALNAME)));
- 2.使用消息模板接收消息:ACLMessage msg=receive(mt);
阻塞行为:我们希望只有在新消息到达时才执行监听消息的行为action(),可以采用Behaviour的block()方法阻塞行为,有新消息插入消息队列时,所有阻塞的行为都会被激活执行action()方法
- ACLMessage msg=receive();if(msg!=null){// process the message}else{block();}
黄页功能
DFAgent提供黄页服务:每个遵从FIPA的平台都默认有一个DFAgent(Directory Facilitator目录服务器的Agent)提供黄页服务
黄页功能设计的三个重要类:
- DFService类(jade.domain.DFService)用于发布服务、搜索服务等
- DFAgentDescription类(jade. domain. FIPAAgentManagement)用于描述自身(如AID,可提供的服务描述列表等)
- ServiceDescription类用于描述每个发布的服务(服务种类、名称、语言、本体等)
发布服务:
- 1.建立黄页描述:DFAgentDescription dfd=new DFAgentDescription();dfd.setName(getAID());
- 2.建立服务描述:ServiceDescription sd=new ServiceDescription();sd.setType(“Book-selling”);sd.setName(getLocalName()+”-Book-selling-“);
- 3.将服务描述添加到黄页描述中:dfd.addService(sd);
- 4.发布服务:DFService.register(this,dfd);
- 5.终止Agent时注销服务:DFService.deregister(this);
搜索服务:
- 1.建立黄页描述:DFAgentDescription dfd=new DFAgentDescription();
- 2.建立服务描述:ServiceDescription sd=new ServiceDescription();sd.setType(“Book-selling”);
- 3.将服务描述添加到黄页描述中:dfd.addService(sd);
- 4.搜索服务:DFAgentDescription[] result=DFService.search(myAgent,dfd);result[0].getName();