How to implement a custom loadbalancing policy for EJB calls in EAP6
Environment
- Red Hat JBoss Enterprise Application Platform (EAP)
- 6.x
- 7.0.x
Issue
- It might be desirable that servers for the application do not handle the same amount of EJB calls in general or for a time period.
- I prefer to use remote servers for EJB invocations, is it possible to customize it?
Resolution
A selector with any custom node selection loadbalancing can be implemented.
There are two interfaces because the selector act different if the application is deployed in a clustered environment or not.
The implementation will receive the name jboss.node.name of all servers that can handle the given EJB request.
It must be set global for the EJBContext at client side.
As example a round-robin policy implementation is shown.
Implementation (non clustered environment)
public class RoundRobinNodeSelector implements DeploymentNodeSelector {
private volatile int lastServer = 0;
@Override
public String selectNode(String[] eligibleNodes, String appName, String moduleName, String distinctName) {
// Just a single node available, so just return it
if (eligibleNodes.length == 1) {
return eligibleNodes[0];
}
if(lastServer >= eligibleNodes.length) {
lastServer=0;
}
return eligibleNodes[lastServer++];
}
implementation (clustered environment)
public class RoundRobinClusterNodeSelector implements ClusterNodeSelector {
private volatile int lastServer = 0;
@Override
public String selectNode(final String clusterName, final String[] connectedNodes, final String[] availableNodes) {
// Just a single node available, so just return it
if (availableNodes.length == 1) {
return availableNodes[0];
}
if(lastServer >= availableNodes.length) {
lastServer=0;
}
return availableNodes[lastServer++];
}
}
Configuration with jboss-ejb-client.properties
Add the property deployment.node.selector with your full qualified DeploymentNodeSelector implementation class name.
The selector will see all configured servers which are available at the invocation time.
deployment.node.selector=org.jboss.example.RoundRobinNodeSelector
remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=false
remote.connections=one,two
remote.connection.one.host=localhost
remote.connection.one.port = 4447
remote.connection.one.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false
remote.connection.one.username=user
remote.connection.one.password=user123
remote.connection.two.host=localhost
remote.connection.two.port = 4547
remote.connection.two.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false
### cluster specific
remote.clusters=ejb
remote.cluster.ejb.clusternode.selector=org.jboss.example.RoundRobinClusterNodeSelector
Using JBoss ejb-client API
Add the property deployment.node.selector must to the list for the PropertiesBasedEJBClientConfiguration constructor.
Properties p = new Properties();
p.put("deployment.node.selector", "org.jboss.example.RoundRobinNodeSelector");
p.put("remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED", "false");
p.put("remote.connections", "one,two");
p.put("remote.connection.one.port", "4447");
p.put("remote.connection.one.host", "localhost");
p.put("remote.connection.two.port", "4547");
p.put("remote.connection.two.host", "localhost");
// if clustered
p.put("remote.clusters", "ejb");
p.put("remote.clusters", "remote.cluster.ejb.clusternode.selector",org.jboss.example.RoundRobinClusterNodeSelector");
EJBClientConfiguration cc = new PropertiesBasedEJBClientConfiguration(p);
ContextSelector<EJBClientContext> selector = new ConfigBasedEJBClientContextSelector(cc);
EJBClientContext.setSelector(selector);
p = new Properties();
p.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
InitialContext context = new InitialContext(p);
Server application side configuration with jboss-ejb-client.xml
If the loadbalancing policy should be used for server-server communication the class can be packaged together with the application (or even as module) and must be configured within the ejb-client settings. The file is located in the META-INF directory of the top-level ear archive.
The ejb-client schema version 1.2 will be availabe for EAP6 Final.
<jboss-ejb-client xmlns="urn:jboss:ejb-client:1.2">
<client-context deployment-node-selector="org.jboss.example.RoundRobinNodeSelector">
<ejb-receivers>
<remoting-ejb-receiver outbound-connection-ref="..."/>
</ejb-receivers>
...
<clusters>
<cluster name="ejb" cluster-node-selector="org.jboss.example.RoundRobinClusterSelector">
...
</cluster>
</clusters>
</client-context>
</jboss-ejb-client>
Important note regarding server names
The servers listed in the EJBContext are distinct by the name.
By default, in standalone mode, this is the name of the machine. In case of having more JBoss standalone instances at the same physical box the jboss.node.name must be set different for all that servers.
If the domain mode is used the server names are configured within the host.xml file and it is ensured that they will be unique.
Root Cause
As there is no proxy provided from the server, as this was in further EAP releases, the client is responsible to select the server.
Releated articles
- The EJB invocations are not well balanced across my clustered EAP6 servers
- Unexpected behaviour of the persistence if EJB's are invoked remote inside of a server application in EAP6
This solution is part of Red Hat’s fast-track publication program, providing a huge library of solutions that Red Hat engineers have created while supporting our customers. To give you the knowledge you need the instant it becomes available, these articles may be presented in a raw and unedited form.