четверг, 11 декабря 2008 г.

Event-Driven Development в Spring. Часть 2

Механизм рассылки event-ов в контексте Spring реализован одним интерфейсом org.springframework.context.event.ApplicationEventMulticaster. Именно ему и будут делегироваться все добавления, удаления listener-ов, а также публикация event-ов для них. По-умолчанию, в качестве реализации такого интерфейса будет использоваться объект класса org.springframework.context.event.SimpleApplicationEventMulticaster. В самом же контексте данный bean регистрируется под именем applicationEventMulticaster. Однако, если под таким именем bean уже существует и реализует нужный интерфейс, то будет использоваться именно он.

Необоходимость создания своего ApplicationEventMulticaster-а может возникнуть, например, чтобы рассылать event-ы по spring-контекстам целого кластера. Именно этот случай и реализован в моем примере. Сам механизм коммуникации между нодами обеспечивается фреймворком JGroups, который рассылает сообщения по сети multicast-ом через UDP-протокол. Объект com.blogspot.nkoksharov.springevents.jgroups.JGroupsMulticaster переопределяет метод void multicastEvent(ApplicationEvent event) и рассылает по сети лишь те экземпляры event-ов, классы которых являются подклассами com.blogspot.nkoksharov.springevents.jgroups.JGroupsEvent. А вот и сам код JGroupsMulticaster-а:

package com.blogspot.nkoksharov.springevents.jgroups;

import org.jgroups.*;
import org.slf4j.*;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.SimpleApplicationEventMulticaster;

/**
* Alternative multicaster to org.springframework.context.event.SimpleApplicationEventMulticaster
*
* @author nkoksharov
*
*/
public class JGroupsMulticaster extends SimpleApplicationEventMulticaster implements InitializingBean, DisposableBean, LocalEventMulticaster {

private final Logger logger = LoggerFactory.getLogger(getClass());
private JChannel jchannel;
private JGroupsReceiver receiver = new JGroupsReceiver();
private String configFile;
private String clusterName;

public void multicastEvent(ApplicationEvent event) {
if (event instanceof JGroupsEvent) {
try {
jchannel.send(new Message(null, null, event));
} catch (ChannelNotConnectedException e) {
logger.error("channel not connected", e);
} catch (ChannelClosedException e) {
logger.error("channel closed", e);
}
} else {
super.multicastEvent(event);
}
}

public void localMulticastEvent(JGroupsEvent event) {
super.multicastEvent(event);
}

public void afterPropertiesSet() throws Exception {
jchannel = new JChannel(configFile);
receiver.setLocalMulticater(this);
jchannel.setReceiver(receiver);
jchannel.connect(clusterName);
}

public void destroy() throws Exception {
jchannel.close();
}
public void setConfigFile(String configFile) {
this.configFile = configFile;
}
public void setClusterName(String clusterName) {
this.clusterName = clusterName;
}
}

Рассылка же абсолютно всех event-ов, в частности системных, может привести к некорректной работе всего spring-контекста.

Проект собирается с помощью Maven-а и запускается командой:

mvn compile exec:java

Каждый запуск - это новый контекст, т.е. можно запустить приложение на разных компьютерах в локальной сети и наблюдать появление сообщений в консоли о получении event-ов. Также можно запустить несколько экземпляров приложения на одном компьютере, все должно работать.

Скачать исходники
Event-Driven Development в Spring. Часть 1

2 комментария:

Satellite13 комментирует...

А как в JGroup ноды находят друг друга?

Никита Кокшаров комментирует...

В документации детально не описан сам механизм. Как я и написал выше используются multicast сообщения. Cодается некий view содержащий информацию об участниках группы. Этот view обновляется и рассылается всем nod-ам, coordinator-ом - "хозяином клястера", по мере появления новых участников.