Load Balancing EJBs in WildFly

One of the main advantages of using clustered Session Beans is that load can be spread across several cluster members. This by default happens on a random basis. Today we will learn how to customize the Session bean load balancing in WildFly or JBoss EAP 6.

By default, calls from a client to a cluster of Stateless Session Beans (SLSB) will be distributed among the SLSBs in a random like behavior. Things are different in case you are using Stateful EJBs (SFSB) and this is discussed in the next part of the article.

As you can see from the following image, before Clustering enters in the picture, when an EJB is invoked a Deployment Node Selector is used to choose a corresponding EJB receiver for an eligible server instance that can handle the EJB invocation.

wildfly ejb ha cluster

The default node selector is a RandomNode Selector. If you want, you can provide a custom DeploymentNodeSelector by implementing the org.jboss.ejb.client.DeploymentNodeSelector. Here is an example of it:

package com.sample;

import org.jboss.ejb.client.DeploymentNodeSelector;

public class RoundRobinDeploymentNodeSelector implements DeploymentNodeSelector {

   private int serverIndex = 0;

   @Override
  public String selectNode(String[] eligibleNodes, String appName, String moduleName,String distinctName)

   {

   String selectedNode = eligibleNodes[serverIndex++ % eligibleNodes.length];

   System.out.println("Selected node: " + selectedNode);

   return selectedNode;

   }

}

The DeploymentNodeSelector can be configured in the jboss-ejb-client.properties file as follows:

deployment.node.selector=com.sample.RoundRobinDeploymentNodeSelector

Include the following dependency in order to compile your Deployment Selector:

<dependency>
	<groupId>org.jboss</groupId>
	<artifactId>jboss-ejb-client</artifactId>
</dependency>

Clustered EJB Scenario

When running in a cluster, after the first invocation (with DeploymentNodeSelector) a cluster-view is returned from the server and the client creates a ClusterContext with all cluster-members and the ClusterNodeSelector. From now on this context is used.

Besides this, in a cluster scenario, an important factor is the type of EJB.

In case of Stateless Session beans, subsequent invocations of the EJBs will be balanced by the ClusterNodeSelector since there is a weak affinity with the server that completed the first call.

wildfly ha cluster ejb jboss

In case of Stateful session beans, there will be an strong affinity with the specific node where the communication started. This means that the ClusterNodeSelector will be bypassed until a new cluster view is returned. In other words, the client will be pinned to the server where the Stateful EJB is, as long as that EJB instance is available.

wildfly ha cluster ejb jboss

That being said, let’s see how to code a cluster node selector. This is a Java class that implements the org.jboss.ejb.client.ClusterNodeSelector. JBoss EAP 6 and WildFly ships with a RandomClusterNodeSelector, that randomly picks up a node in the cluster.

Using a RandomClusterNodeSelector cloud be a pitfall as it is a non predictable algorithm for choosing the EJB that will be invoked. So, in order to overcome these issues, a class which implements the ClusterNodeSelector can be made available on the client application side. Here is an example, which uses a Round Robin Node Selector:

public class RoundRobinNodeSelector implements ClusterNodeSelector {

 private AtomicInteger clusterNode;

 public RoundRobinNodeSelector() {

  clusterNode = new AtomicInteger(0);

 }

 @Override

 public String selectNode(String clusterName,

  String[] connectedNodes, String[] availableNodes) {

  if (availableNodes.length < 2) {
 return availableNodes[0];

  }

  return availableNodes[clusterNode.getAndIncrement() % availableNodes.length];

 }

}

Now let’s see in detail the example:

The selectedNode is invoked when a new EJB needs to be reached in the cluster. It receives the current clusterName. The String[] availableNodes contains all registered nodes which are able to handle the current EJB invocation.

Finally, connectedNodes contains all the already connected nodes and is a subset of availableNodes.

In the above example, the class named RoundRobinNodeSelector uses a RoundRobin algorithm by cycling over an index that is incremented on each call

Please note that we need to use an AtomicInteger object as we need a thread-safe, atomically incremented counter. This is performed via the getAndIncrement method.

Now let’s see how to configure the selector in two possible scenarios:

Remote Java clientFor remote Java clients you have to include in your jboss-ejb-client.properties a reference to your selector and the:

remote.clusters=ejb

remote.cluster.ejb.clusternode.selector=com.sample.RoundRobinNodeSelector

If the EJBClientContext is set programmaticaly

Properties p = new Properties();

p.put(.......)  // other properties

p.put("remote.clusters", "ejb");

p.put("remote.cluster.ejb.clusternode.selector", RoundRobinNodeSelector.class.getName());

EJBClientConfiguration cc = new PropertiesBasedEJBClientConfiguration(p);

ContextSelector<EJBClientContext> selector = new ConfigBasedEJBClientContextSelector(cc);

EJBClientContext.setSelector(selector);

If the remote EJB client is part of the Java EE application (let’s say a Servlet) the outbound connections are configured in the jboss-ejb-client.xml descriptor. The Selector needs to be added to the cluster configuration as follows:

<jboss-ejb-client xmlns:p="urn:jboss:ejb-client:1.2" xmlns:xsi="htt//www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:jboss:ejb-client:1.2 sample.xsd ">

  <client-context>

 <clusters>

  <cluster name="ejb" cluster-node-selector="com.sample.RoundRobinNodeSelector" max-allowed-connected-nodes="25" connect-timeout="5000" >

  </cluster>

  </clusters>

  </client-context>

</jboss-ejb-client>

Please notice we have used the max number of nodes to override the default implementation which allows just 20 nodes.

Porting JBoss EAP 5 applications to WildFly

Last but not least we will mention where load balancing was configured in the earlier version of the application server. This was done the jboss.xml, by means of the load-balance-policy attribute as follows:

<jboss>  

 <enterprise-beans>

  <session>

 <ejb-name>NonAnnotationStateful</ejb-name>

 <clustered>true</clustered>

 <cluster-config>
  <partition-name>FooPartition</partition-name>
  <load-balance-policy>
 org.jboss.ha.framework.interfaces.RandomRobin
  </load-balance-policy>
 </cluster-config>

  </session>  

</enterprise-beans>

</jboss>

Therefore if you have coded your own load balacing policy then you can move it into the Deployment Node Selector or Cluster Node Selector as described in this article.

Found the article helpful? if so please follow us on Socials