Получение сообщений в блокирующем режиме
Помимо метода receive() класс Agent также предоставляет метод blockingReceive(), который, как можно предположить по его имени, является блокирующим вызовом — он не возвращает управление до тех пор, пока не появится сообщение в очереди сообщений агента. Также существует перегруженная версия этого метода, которая получает в качестве аргумента MessageTemplate (она не возвращает управления до тех пор, пока нет сообщения, удовлетворяющего шаблону).
Важно подчеркнуть, что метод blockingReceive() фактически блокирует поток агента. Поэтому, если вызывается blockingReceive() «из поведения», это прекратит выполнение других поведений до тех пор, пока вызов blockingReceive() не вернёт управление. Принимая во внимание эти соображения, можно отметить, что хорошим тоном программирования процесса получения сообщений будет: использование blockingReceive() в методах setup() и takeDown(); использование receive() в сочетании с Behaviour.block() (как показано в 3.5) внутри поведения.
-
Сервис «Желтых страниц» - Класс DFService
В приведенном коде примера было сделано допущение, что существует некое фиксированное множество агентов-продавцов (seller1 и seller2), и каждый покупатель заранее знает о них (см. поле AID[] sellerAgents класса BookBuyerAgent в коде, представленном в главе 2.4). В этой главе описано как избавиться от этого допущения и использовать сервис «жёлтых страниц», предоставленный платформой JADE для того, чтобы позволить агентам-покупателям динамически узнавать об агентах-продавцах, доступных в данный момент.
Сервис «Жёлтых страниц» позволяет агентам опубликовать данные об одном или более сервисах, которые они предоставляют, так, что другие агенты могут находить и успешно использовать эти сервисы (см. рис. 4).
Рис. 4. Сервис Желтых страниц
Сервис «жёлтых страниц» в JADE (согласно спецификации FIPA) предоставлен агентом, названным DF (Directory Facilitator, Менеджер Директорий). Каждая соответствующая FIPA платформа поддерживает по умолчанию агент DF (его локальное имя — «df»). Другие DF-агенты могут быть запущены, и несколько DF-агентов (включая те, что находятся в платформе по умолчанию) можно объединить для обеспечения единого распределённого каталога «жёлтых страниц».
Поскольку DF является агентом, имеется возможность взаимодействовать с ним обычным образом, обмениваясь ACL-сообщениями, используя соответствующий язык содержания (язык SLo) и соответствующую онтологию (онтологию FIPA-agent-management), согласно спецификациям FIPA. Для упрощения этих взаимоотношений JADE предоставляет класс jade.domain.DFService, с помощью которого можно опубликовывать и искать сервисы через вызовы методов.
Агент, желающий опубликовать (сделать доступными публично) один или более сервисов должен предоставить DF описание, включающее, AID этого агента, список возможных языков и онтологий, которые должны знать другие агенты для взаимодействия с ним и список сервисов для публикации. Для каждого опубликовываемого сервиса предоставляется описание, включающее тип сервиса, его имя, языки и онтологии, необходимые для его использования и ещё некоторое количество свойств, специфичных для данного сервиса. Классы DFAgentDescription, ServiceDescription и Property, включённые в пакет jade.domain.FIPAAgentManagement, соответственно представляют собой три упомянутые абстракции.
Чтобы опубликовать сервис агент должен создать подходящее описание (представленное экземпляром класса DFAgentDescription) и вызвать статический метод register() класса DFService. Обычно (но не обязательно) регистрация сервиса проходит в методе setup(), как показано ниже в случае с агентом-продавцом книг (этот код добавляем в метод setup() класса BookSellerAgent).
protected void setup() {
........... //Создаём и показываем GUI
myGui = new BookSellerGui(this);
myGui.showGui();
// Register the book-selling service in the yellow pages
DFAgentDescription dfd = new DFAgentDescription();
dfd.setName(getAID());
ServiceDescription sd = new ServiceDescription();
sd.setType("book-selling");
sd.setName("JADE-book-trading");
dfd.addServices(sd);
try {
DFService.register(this, dfd);
}
catch (FIPAException fe) {
fe.printStackTrace();
}
...........
}
Замечание. В этом простом примере не были описаны ни языки, ни онтологии, ни свойства, специфичные для данного сервиса. Когда агент завершается хорошим тоном будет отменить регистрацию опубликованных сервисов (изменяем метод takeDown() в фйле BookSellerAgent).
//Вставляем чистящие операции агента
protected void takeDown() {
//Отмена регистрации сервиса из «жёлтых страниц»
try {
DFService.deregister(this);
}
catch (FIPAException fe) {
fe.printStackTrace();
}
// Закрываем GUI
myGui.dispose();
// Вывод на экран сообщения о завершении
System.out.println("Seller-agent "+getAID().getName()+" terminating.");
}
}
Агент, желающий искать некоторые сервисы, должен предоставить DF описание необходимого шаблона. Результат поиска — список всех описаний, которые удовлетворяют указанному шаблону. Описание соответствует шаблону, если все поля, указанные в шаблоне, присутствуют в описании, причём с теми же значениями.
Использование статического метод search() класса DFService может быть проиллюстрировано примером, когда агент-покупатель книг динамически находит всех агентов, которые предоставляют сервис «book-selling» (продажа книг).
public class BookBuyerAgent extends Agent {
private static final long serialVersionUID = 8257866411543354399L;
// Название покупаемой книги
private String targetBookTitle;
// Лист известных агентов-продавцов
private AID[] sellerAgents = { new AID("seller1", AID.ISLOCALNAME),
new AID("seller2", AID.ISLOCALNAME)};
// Тут инициализация агента
protected void setup() {
// Выводим приветствие
System.out.println("Привет! Агент-покупатель"+getAID().getName()+"готов.");
//Получение названия покупаемой книги как аргумент при старте
Object[] args = getArguments();
if (args != null && args.length > 0) {
targetBookTitle = (String) args[0];
System.out.println("Target book is "+targetBookTitle);
// Добавляем TickerBehaviour где планируется запрос агенту продавцу каждую минуту
addBehaviour(new TickerBehaviour(this, 60000) {
protected void onTick() {
System.out.println("Trying to buy " +targetBookTitle);
// Обновление списка агентов агентов-продавцов
DFAgentDescription template = new DFAgentDescription();
ServiceDescription sd = new ServiceDescription();
sd.setType("book-selling");
template.addServices(sd);
try {
DFAgentDescription[] result = DFService.search(myAgent, template);
sellerAgents = new AID[result.length];
for (int i = 0; i < result.length; ++i) {
sellerAgents[i] = result[i].getName();
System.out.println(sellerAgents[i].getName());
}
}
catch (FIPAException fe) {
fe.printStackTrace();
}
// Выполнение запроса
myAgent.addBehaviour(new RequestPerformer());
}
} );
}
else {
// Завершение выполнения агента немедлено
System.out.println("Нет заданой книги");
doDelete();
}
}
Замечание. Обновление списка известных агентов-продавцов производится каждый раз перед попыткой купить указанную книгу, поскольку агенты-продавцы в системе могут динамически появляться и исчезать. Класс DFService также предоставляет поддержку подписки на уведомления от DF о появление агента, предоставляющего указанный сервис (более детально — методы searchUntilFound() и createSubscriptionMessage()), но это выходит за рамки данного руководства.
|