WildFly 8 introduces a new API which can be used to gather information about the WildFly cluster topology and receive notifications when new nodes leave or join the cluster.
The org.wildfly.clustering.group.Group interface can be used for this purpose:
The Group service provides a mechanism to view the cluster topology and to be notified when the topology changes. You can monitor both a JGroup channel and a Cache group. The purpose of this tutorial will be to monitor JGroups channels.
A Channel represents a group communication endpoint. A client joins a group by connecting the channel to a group and leaves it by disconnecting. Messages sent over the channel are received by all group members that are connected to the same group (that is, all members that have the same group name).
Let’s start a cluster aware WildFly application server at first. Now let’s dig a bit under the jgroups subsystem to check the available channels:
/subsystem=jgroups/:read-resource(recursive-depth=0) { "outcome" => "success", "result" => { "default-stack" => "udp", "channel" => { "ejb" => undefined, "web" => undefined }, "stack" => { "udp" => undefined, "tcp" => undefined } } }
So, out of the box the “ejb” and the “web” channels are available. Now let’s deploy a simple Web application which references this EJB:
import java.util.List; import javax.annotation.Resource; import javax.ejb.Stateless; import org.wildfly.clustering.group.Group; import org.wildfly.clustering.group.Node; @Stateless public class EJBGroup { @Resource(lookup = "java:jboss/clustering/group/web") private Group channelGroup; public void check() { List<Node> nodes = channelGroup.getNodes(); for (Node node: nodes) System.out.println(node.getName() + " "+node.getSocketAddress()); } }
As you can see, the EJB prints out in the check method the list of nodes which are part of the cluster. This information is obtained through the org.wildfly.clustering.group.Group instance that is injected in the EJB. Now include a basic servlet client within the Web application:
@WebServlet("/servlet") public class DemoServlet extends HttpServlet { . . . @EJB EJBGroup ejb; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ejb.checkNodes(); } }
When you run the application, the following information will be displayed on the Console:
[Server:server-one] 12:10:48,673 INFO [stdout] (default task-3) master:server-one/web /127.0.0.1:55200 [Server:server-one] 12:10:48,673 INFO [stdout] (default task-3) master:server-three/web /127.0.0.1:55450 [Server:server-one] 12:10:48,673 INFO [stdout] (default task-3) master:server-two/web /127.0.0.1:55350
In our case, we have deployed the application in a Domain using HA profiles with 3 nodes available.
Receiving notification from cluster changes
Most interesting for us is the option to receive notifications via listeners when the cluster topology changes. This can be achieved via the org.wildfly.clustering.group.Group.Listener interface. We will add a Singleton EJB which registers a listener as soon as the application is deployed.
@Startup public class SingletonEJB { @Resource(lookup = "java:jboss/clustering/group/web") private Group channelGroup; @PostConstruct public void check() { channelGroup.addListener(new MyListener ()); } }
And here is the MyListener class:
import java.util.List; import org.wildfly.clustering.group.Group.Listener; import org.wildfly.clustering.group.Node; public class MyListener implements Listener { @Override public void membershipChanged(List<Node> prev, List<Node> curr, boolean merge) { for (Node node: prev) System.out.println("PREVIOUS CUSTER VIEW: " +node.getName() + " "+node.getSocketAddress()); System.out.println("=================================================="); for (Node node: curr) System.out.println("NEW CLUSTER VIEW " +node.getName() + " "+node.getSocketAddress()); System.out.println("=================================================="); System.out.println("Merged ? "+merge); } }
As you can see, the Listener class has to implement the membershipChanged method which receives as input the following parameters:
- previousMembers previous group members
- members new group members
- merged indicates whether the membership change is the result of a merge view
Deploy your application and verify that the listener kicks in as soon as you start and stop cluster nodes. Here’s the aftermath of starting a new cluster node (server-two)
[Server:server-one] 12:09:37,820 INFO [stdout] (notification-thread-0) PREVIOUS CUSTER VIEW: master:server-one/web /127.0.0.1:55200 [Server:server-one] 12:09:37,820 INFO [stdout] (notification-thread-0) PREVIOUS CUSTER VIEW: master:server-three/web /127.0.0.1:55450 [Server:server-one] 12:09:37,835 INFO [stdout] (notification-thread-0) ================================================== [Server:server-one] 12:09:37,835 INFO [stdout] (notification-thread-0) NEW CLUSTER VIEW master:server-one/web /127.0.0.1:55200 [Server:server-one] 12:09:37,835 INFO [stdout] (notification-thread-0) NEW CLUSTER VIEW master:server-three/web /127.0.0.1:55450 [Server:server-one] 12:09:37,835 INFO [stdout] (notification-thread-0) NEW CLUSTER VIEW master:server-two/web /127.0.0.1:55350