8.10. 为 Jakarta Enterprise Beans 调用实施自定义负载平衡策略
jboss-ejb-client.properties 文件的使用已弃用,而是使用 wildfly-config.xml 文件。
可以实施备用或自定义负载平衡策略,以平衡应用的 Jakarta 企业 Beans 调用服务器。
您可以为 Jakarta Enterprise Beans 调用实施 AllClusterNodeSelector。AllClusterNodeSelector 的节点选择行为类似于默认选择器,但 AllClusterNodeSelector 使用所有可用的集群节点(即使大型集群(node 的数量 > 20)。如果返回一个未连接的集群节点,它会自动打开。以下示例演示了 AllClusterNodeSelector 实现:
package org.jboss.as.quickstarts.ejb.clients.selector;
import java.util.Arrays;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jboss.ejb.client.ClusterNodeSelector;
public class AllClusterNodeSelector implements ClusterNodeSelector {
private static final Logger LOGGER = Logger.getLogger(AllClusterNodeSelector.class.getName());
@Override
public String selectNode(final String clusterName, final String[] connectedNodes, final String[] availableNodes) {
if(LOGGER.isLoggable(Level.FINER)) {
LOGGER.finer("INSTANCE "+this+ " : cluster:"+clusterName+" connected:"+Arrays.deepToString(connectedNodes)+" available:"+Arrays.deepToString(availableNodes));
}
if (availableNodes.length == 1) {
return availableNodes[0];
}
final Random random = new Random();
final int randomSelection = random.nextInt(availableNodes.length);
return availableNodes[randomSelection];
}
}
package org.jboss.as.quickstarts.ejb.clients.selector;
import java.util.Arrays;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jboss.ejb.client.ClusterNodeSelector;
public class AllClusterNodeSelector implements ClusterNodeSelector {
private static final Logger LOGGER = Logger.getLogger(AllClusterNodeSelector.class.getName());
@Override
public String selectNode(final String clusterName, final String[] connectedNodes, final String[] availableNodes) {
if(LOGGER.isLoggable(Level.FINER)) {
LOGGER.finer("INSTANCE "+this+ " : cluster:"+clusterName+" connected:"+Arrays.deepToString(connectedNodes)+" available:"+Arrays.deepToString(availableNodes));
}
if (availableNodes.length == 1) {
return availableNodes[0];
}
final Random random = new Random();
final int randomSelection = random.nextInt(availableNodes.length);
return availableNodes[randomSelection];
}
}
您还可以为 Jakarta Enterprise Beans 调用实施 SimpleLoadFactorNodeSelector。SimpleLoadFactorNodeSelector 中的 负载均衡会根据负载因数进行。负载因数(2/3/4)根据节点的名称(A/B/C)计算,不论每个节点上的负载如何。以下示例显示了 SimpleLoadFactorNodeSelector 实现:
package org.jboss.as.quickstarts.ejb.clients.selector;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jboss.ejb.client.DeploymentNodeSelector;
public class SimpleLoadFactorNodeSelector implements DeploymentNodeSelector {
private static final Logger LOGGER = Logger.getLogger(SimpleLoadFactorNodeSelector.class.getName());
private final Map<String, List<String>[]> nodes = new HashMap<String, List<String>[]>();
private final Map<String, Integer> cursor = new HashMap<String, Integer>();
private ArrayList<String> calculateNodes(Collection<String> eligibleNodes) {
ArrayList<String> nodeList = new ArrayList<String>();
for (String string : eligibleNodes) {
if(string.contains("A") || string.contains("2")) {
nodeList.add(string);
nodeList.add(string);
} else if(string.contains("B") || string.contains("3")) {
nodeList.add(string);
nodeList.add(string);
nodeList.add(string);
} else if(string.contains("C") || string.contains("4")) {
nodeList.add(string);
nodeList.add(string);
nodeList.add(string);
nodeList.add(string);
}
}
return nodeList;
}
@SuppressWarnings("unchecked")
private void checkNodeNames(String[] eligibleNodes, String key) {
if(!nodes.containsKey(key) || nodes.get(key)[0].size() != eligibleNodes.length || !nodes.get(key)[0].containsAll(Arrays.asList(eligibleNodes))) {
// must be synchronized as the client might call it concurrent
synchronized (nodes) {
if(!nodes.containsKey(key) || nodes.get(key)[0].size() != eligibleNodes.length || !nodes.get(key)[0].containsAll(Arrays.asList(eligibleNodes))) {
ArrayList<String> nodeList = new ArrayList<String>();
nodeList.addAll(Arrays.asList(eligibleNodes));
nodes.put(key, new List[] { nodeList, calculateNodes(nodeList) });
}
}
}
}
private synchronized String nextNode(String key) {
Integer c = cursor.get(key);
List<String> nodeList = nodes.get(key)[1];
if(c == null || c >= nodeList.size()) {
c = Integer.valueOf(0);
}
String node = nodeList.get(c);
cursor.put(key, Integer.valueOf(c + 1));
return node;
}
@Override
public String selectNode(String[] eligibleNodes, String appName, String moduleName, String distinctName) {
if (LOGGER.isLoggable(Level.FINER)) {
LOGGER.finer("INSTANCE " + this + " : nodes:" + Arrays.deepToString(eligibleNodes) + " appName:" + appName + " moduleName:" + moduleName
+ " distinctName:" + distinctName);
}
// if there is only one there is no sense to choice
if (eligibleNodes.length == 1) {
return eligibleNodes[0];
}
final String key = appName + "|" + moduleName + "|" + distinctName;
checkNodeNames(eligibleNodes, key);
return nextNode(key);
}
}
package org.jboss.as.quickstarts.ejb.clients.selector;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jboss.ejb.client.DeploymentNodeSelector;
public class SimpleLoadFactorNodeSelector implements DeploymentNodeSelector {
private static final Logger LOGGER = Logger.getLogger(SimpleLoadFactorNodeSelector.class.getName());
private final Map<String, List<String>[]> nodes = new HashMap<String, List<String>[]>();
private final Map<String, Integer> cursor = new HashMap<String, Integer>();
private ArrayList<String> calculateNodes(Collection<String> eligibleNodes) {
ArrayList<String> nodeList = new ArrayList<String>();
for (String string : eligibleNodes) {
if(string.contains("A") || string.contains("2")) {
nodeList.add(string);
nodeList.add(string);
} else if(string.contains("B") || string.contains("3")) {
nodeList.add(string);
nodeList.add(string);
nodeList.add(string);
} else if(string.contains("C") || string.contains("4")) {
nodeList.add(string);
nodeList.add(string);
nodeList.add(string);
nodeList.add(string);
}
}
return nodeList;
}
@SuppressWarnings("unchecked")
private void checkNodeNames(String[] eligibleNodes, String key) {
if(!nodes.containsKey(key) || nodes.get(key)[0].size() != eligibleNodes.length || !nodes.get(key)[0].containsAll(Arrays.asList(eligibleNodes))) {
// must be synchronized as the client might call it concurrent
synchronized (nodes) {
if(!nodes.containsKey(key) || nodes.get(key)[0].size() != eligibleNodes.length || !nodes.get(key)[0].containsAll(Arrays.asList(eligibleNodes))) {
ArrayList<String> nodeList = new ArrayList<String>();
nodeList.addAll(Arrays.asList(eligibleNodes));
nodes.put(key, new List[] { nodeList, calculateNodes(nodeList) });
}
}
}
}
private synchronized String nextNode(String key) {
Integer c = cursor.get(key);
List<String> nodeList = nodes.get(key)[1];
if(c == null || c >= nodeList.size()) {
c = Integer.valueOf(0);
}
String node = nodeList.get(c);
cursor.put(key, Integer.valueOf(c + 1));
return node;
}
@Override
public String selectNode(String[] eligibleNodes, String appName, String moduleName, String distinctName) {
if (LOGGER.isLoggable(Level.FINER)) {
LOGGER.finer("INSTANCE " + this + " : nodes:" + Arrays.deepToString(eligibleNodes) + " appName:" + appName + " moduleName:" + moduleName
+ " distinctName:" + distinctName);
}
// if there is only one there is no sense to choice
if (eligibleNodes.length == 1) {
return eligibleNodes[0];
}
final String key = appName + "|" + moduleName + "|" + distinctName;
checkNodeNames(eligibleNodes, key);
return nextNode(key);
}
}
配置 jboss-ejb-client.properties 文件
您需要使用实现类(AllClusterNodeSelector 或 选择器将显示调用时可用的所有已配置服务器。以下示例使用 SimpleLoadF 属性。actorNodeSelector )的名称添加 remote.cluster.ejb.clusternode.selectorAllClusterNodeSelector 作为集群节点选择器:
remote.clusters=ejb remote.cluster.ejb.clusternode.selector=org.jboss.as.quickstarts.ejb.clients.selector.AllClusterNodeSelector remote.cluster.ejb.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false remote.cluster.ejb.connect.options.org.xnio.Options.SSL_ENABLED=false remote.cluster.ejb.username=test remote.cluster.ejb.password=password remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=false remote.connections=one,two remote.connection.one.host=localhost remote.connection.one.port = 8080 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 = 8180 remote.connection.two.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false
remote.clusters=ejb
remote.cluster.ejb.clusternode.selector=org.jboss.as.quickstarts.ejb.clients.selector.AllClusterNodeSelector
remote.cluster.ejb.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false
remote.cluster.ejb.connect.options.org.xnio.Options.SSL_ENABLED=false
remote.cluster.ejb.username=test
remote.cluster.ejb.password=password
remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=false
remote.connections=one,two
remote.connection.one.host=localhost
remote.connection.one.port = 8080
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 = 8180
remote.connection.two.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false
使用 Jakarta Enterprise Beans 客户端 API
您需要将属性 remote.cluster.ejb.clusternode.selector 添加到 PropertiesBasedEJBClientConfiguration 构造器的列表中。以下示例使用 AllClusterNodeSelector 作为集群节点选择器:
Properties p = new Properties();
p.put("remote.clusters", "ejb");
p.put("remote.cluster.ejb.clusternode.selector", "org.jboss.as.quickstarts.ejb.clients.selector.AllClusterNodeSelector");
p.put("remote.cluster.ejb.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS", "false");
p.put("remote.cluster.ejb.connect.options.org.xnio.Options.SSL_ENABLED", "false");
p.put("remote.cluster.ejb.username", "test");
p.put("remote.cluster.ejb.password", "password");
p.put("remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED", "false");
p.put("remote.connections", "one,two");
p.put("remote.connection.one.port", "8080");
p.put("remote.connection.one.host", "localhost");
p.put("remote.connection.two.port", "8180");
p.put("remote.connection.two.host", "localhost");
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);
Properties p = new Properties();
p.put("remote.clusters", "ejb");
p.put("remote.cluster.ejb.clusternode.selector", "org.jboss.as.quickstarts.ejb.clients.selector.AllClusterNodeSelector");
p.put("remote.cluster.ejb.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS", "false");
p.put("remote.cluster.ejb.connect.options.org.xnio.Options.SSL_ENABLED", "false");
p.put("remote.cluster.ejb.username", "test");
p.put("remote.cluster.ejb.password", "password");
p.put("remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED", "false");
p.put("remote.connections", "one,two");
p.put("remote.connection.one.port", "8080");
p.put("remote.connection.one.host", "localhost");
p.put("remote.connection.two.port", "8180");
p.put("remote.connection.two.host", "localhost");
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);
配置 jboss-ejb-client.xml 文件
要使用服务器负载平衡策略以进行服务器通信,请将类与应用打包,并在位于 META-INF 文件夹下的 jboss-ejb-client.xml 设置中进行配置。以下示例使用 AllClusterNodeSelector 作为集群节点选择器:
<jboss-ejb-client xmlns:xsi="urn:jboss:ejb-client:1.2" xsi:noNamespaceSchemaLocation="jboss-ejb-client_1_2.xsd">
<client-context deployment-node-selector="org.jboss.ejb.client.DeploymentNodeSelector">
<ejb-receivers>
<!-- This is the connection to access the application. -->
<remoting-ejb-receiver outbound-connection-ref="remote-ejb-connection-1" />
</ejb-receivers>
<!-- Specify the cluster configurations applicable for this client context -->
<clusters>
<!-- Configure the cluster of remote-ejb-connection-1. -->
<cluster name="ejb" security-realm="ejb-security-realm-1" username="test" cluster-node-selector="org.jboss.as.quickstarts.ejb.clients.selector.AllClusterNodeSelector">
<connection-creation-options>
<property name="org.xnio.Options.SSL_ENABLED" value="false" />
<property name="org.xnio.Options.SASL_POLICY_NOANONYMOUS" value="false" />
</connection-creation-options>
</cluster>
</clusters>
</client-context>
</jboss-ejb-client>
<jboss-ejb-client xmlns:xsi="urn:jboss:ejb-client:1.2" xsi:noNamespaceSchemaLocation="jboss-ejb-client_1_2.xsd">
<client-context deployment-node-selector="org.jboss.ejb.client.DeploymentNodeSelector">
<ejb-receivers>
<!-- This is the connection to access the application. -->
<remoting-ejb-receiver outbound-connection-ref="remote-ejb-connection-1" />
</ejb-receivers>
<!-- Specify the cluster configurations applicable for this client context -->
<clusters>
<!-- Configure the cluster of remote-ejb-connection-1. -->
<cluster name="ejb" security-realm="ejb-security-realm-1" username="test" cluster-node-selector="org.jboss.as.quickstarts.ejb.clients.selector.AllClusterNodeSelector">
<connection-creation-options>
<property name="org.xnio.Options.SSL_ENABLED" value="false" />
<property name="org.xnio.Options.SASL_POLICY_NOANONYMOUS" value="false" />
</connection-creation-options>
</cluster>
</clusters>
</client-context>
</jboss-ejb-client>
要将上述配置与安全性一起使用,您需要将 ejb-security-realm-1 添加到客户端-服务器配置。下例演示了添加安全域的 CLI 命令(ejb-security-realm-1)该值是用户 "test" 的 base64 编码密码:
/core-service=management/security-realm=ejb-security-realm-1:add() /core-service=management/security-realm=ejb-security-realm-1/server-identity=secret:add(value=cXVpY2sxMjMr)
/core-service=management/security-realm=ejb-security-realm-1:add()
/core-service=management/security-realm=ejb-security-realm-1/server-identity=secret:add(value=cXVpY2sxMjMr)
如果负载平衡策略应用于服务器通信,可以将类与应用一起打包,或打包为模块。此类配置在顶级 EAR 存档的 META 设置文件中。以下示例使用 -INF 目录中的 jboss- ejb-clientroundRobinNodeSelector 作为部署节点选择器。
<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>
...
</client-context>
</jboss-ejb-client>
<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>
...
</client-context>
</jboss-ejb-client>
如果您正在运行单机服务器,请使用 start 选项 -Djboss.node.name= 或服务器配置文件 standalone.xml 来配置服务器名称。确保服务器名称唯一。如果您正在运行受管域,主机控制器会自动验证名称是否唯一。