8.10. Jakarta Enterprise Beans 呼び出しのカスタムロードバランシングポリシーの実装
jboss-ejb-client.properties ファイルの使用は非推奨となり、wildfly-config.xml ファイルが優先されるようになりました。
アプリケーションの Jakarta Enterprise Beans 呼び出しをサーバー間で分散するために、代替またはカスタマイズしたロードバランシングポリシーを実装することができます。
Jakarta Enterprise Beans 呼び出しに AllClusterNodeSelector を実装することができます。AllClusterNodeSelector のノード選択の動作はデフォルトのセレクターと似ていますが、大規模なクラスター (ノード数 > 20) の場合でも AllClusterNodeSelector は利用可能なすべてのクラスターノードを使用する点が異なります。接続されていないクラスターノードが返されると、そのクラスターノードは自動的に開きます。以下の例は、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];
}
}
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);
}
}jboss-ejb-client.properties ファイルの設定
remote.cluster.ejb.clusternode.selector プロパティーを実装クラスの名前 (AllClusterNodeSelector または SimpleLoadfactorNodeSelector) とともに追加する必要があります。セレクターは、呼び出し時に利用可能な設定済みのサーバーをすべて表示します。以下の例では、AllClusterNodeSelector をクラスターノードセレクターとして使用します。
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);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>
上記のセキュリティー設定を使用するには、client-server 設定に 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)
サーバー間の通信に負荷分散ポリシーを使用する場合は、クラスをアプリケーションとともにパッケージ化するか、モジュールとしてパッケージ化できます。このクラスは、トップレベルの EAR アーカイブの META-INF ディレクトリーにある jboss-ejb-client 設定ファイルで設定されます。以下の例では RoundRobinNodeSelector をデプロイメントノードセレクターとして使用します。
<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>
スタンドアロンサーバーを実行している場合は、サーバー名の設定にオプション -Djboss.node.name= またはサーバー設定ファイル standalone.xml を使用します。サーバー名が一意であることを確認します。管理対象ドメインを実行している場合は、ホストコントローラーは自動的に名前が一意であることを検証します。