[ 
https://issues.apache.org/jira/browse/ARTEMIS-3365?focusedWorklogId=634216&page=com.atlassian.jira.plugin.system.issuetabpanels:worklog-tabpanel#worklog-634216
 ]

ASF GitHub Bot logged work on ARTEMIS-3365:
-------------------------------------------

                Author: ASF GitHub Bot
            Created on: 05/Aug/21 11:26
            Start Date: 05/Aug/21 11:26
    Worklog Time Spent: 10m 
      Work Description: gemmellr commented on a change in pull request #3634:
URL: https://github.com/apache/activemq-artemis/pull/3634#discussion_r682718787



##########
File path: 
tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/balancing/TargetKeyTest.java
##########
@@ -0,0 +1,162 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.activemq.artemis.tests.integration.balancing;
+
+import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants;
+import 
org.apache.activemq.artemis.core.server.balancing.policies.FirstElementPolicy;
+import org.apache.activemq.artemis.core.server.balancing.policies.Policy;
+import 
org.apache.activemq.artemis.core.server.balancing.policies.PolicyFactory;
+import 
org.apache.activemq.artemis.core.server.balancing.policies.PolicyFactoryResolver;
+import org.apache.activemq.artemis.core.server.balancing.targets.Target;
+import org.apache.activemq.artemis.core.server.balancing.targets.TargetKey;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import javax.jms.Connection;
+import javax.jms.ConnectionFactory;
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+@RunWith(Parameterized.class)
+public class TargetKeyTest extends BalancingTestBase {
+
+   private static final String MOCK_POLICY_NAME = "MOCK_POLICY";
+
+   @Parameterized.Parameters(name = "protocol: {0}")
+   public static Collection<Object[]> data() {
+      Collection<Object[]> data = new ArrayList<>();
+
+      for (String protocol : Arrays.asList(new String[] {AMQP_PROTOCOL, 
CORE_PROTOCOL, OPENWIRE_PROTOCOL})) {
+         data.add(new Object[] {protocol});
+      }
+
+      return data;
+   }
+
+
+   private final String protocol;
+
+   private final List<String> keys = new ArrayList<>();
+
+
+   public TargetKeyTest(String protocol) {
+      this.protocol = protocol;
+   }
+
+   @Before
+   public void setup() throws Exception {
+      PolicyFactoryResolver.getInstance().registerPolicyFactory(
+         new PolicyFactory() {
+            @Override
+            public String[] getSupportedPolicies() {
+               return new String[] {MOCK_POLICY_NAME};
+            }
+
+            @Override
+            public Policy createPolicy(String policyName) {
+               return new FirstElementPolicy(MOCK_POLICY_NAME) {
+                  @Override
+                  public Target selectTarget(List<Target> targets, String key) 
{
+                     keys.add(key);
+                     return super.selectTarget(targets, key);
+                  }
+               };
+            }
+         });
+   }
+
+   @Test
+   public void testClientIDKey() throws Exception {
+      setupLiveServerWithDiscovery(0, GROUP_ADDRESS, GROUP_PORT, true, true, 
false);
+      setupBalancerServerWithDiscovery(0, TargetKey.CLIENT_ID, 
MOCK_POLICY_NAME, null, true, null, 1);
+      startServers(0);
+
+      ConnectionFactory connectionFactory = createFactory(protocol, false, 
TransportConstants.DEFAULT_HOST,
+         TransportConstants.DEFAULT_PORT + 0, "test", null, null);
+
+      keys.clear();
+
+      try (Connection connection = connectionFactory.createConnection()) {
+         connection.start();
+      }
+
+      Assert.assertEquals(1, keys.size());
+      Assert.assertEquals("test", keys.get(0));
+   }
+
+   @Test
+   public void testSNIHostKey() throws Exception {
+      setupLiveServerWithDiscovery(0, GROUP_ADDRESS, GROUP_PORT, true, true, 
false);
+      
getDefaultServerAcceptor(0).getParams().put(TransportConstants.SSL_ENABLED_PROP_NAME,
 true);
+      
getDefaultServerAcceptor(0).getParams().put(TransportConstants.KEYSTORE_PATH_PROP_NAME,
 "verified-localdomain-keystore.jks");
+      
getDefaultServerAcceptor(0).getParams().put(TransportConstants.KEYSTORE_PASSWORD_PROP_NAME,
 "secureexample");
+
+      setupBalancerServerWithDiscovery(0, TargetKey.SNI_HOST, 
MOCK_POLICY_NAME, null, true, null, 1);
+      startServers(0);
+
+      ConnectionFactory connectionFactory = createFactory(protocol, true, 
"localhost.localdomain",

Review comment:
       Using "localhost.localdomain" can sometimes be problematic in some 
environments. I'd consider just using localhost for the connections. 
   
   For the Core client, I think you can specify the SNI value explicitly, so 
you could still set it to something entirely different in that case if wanting 
to verify a non-localhost SNI value is picked up by the policy at the server.

##########
File path: examples/features/broker-balancer/symmetric-redirect/readme.md
##########
@@ -0,0 +1,5 @@
+# AMQP Broker Connection with Receivers
+
+To run the example, simply type **mvn verify** from this directory, or **mvn 
-PnoServer verify** if you want to create and start the broker manually.
+
+This example demonstrates how you can distribute incoming client connections 
across two brokers using a symmetric architecture.

Review comment:
       Perhaps expand on what the 'symmetric architecture' is? I honestly have 
no clear idea from the description at this point what the example is about to 
show.
   
   (EDIT: so its showing that both brokers are set up to do a consistent hash 
of ClientID, such that each will direct the same ClientIDs to the same place, 
perhaps itself...which per the other comments, isnt actually great for matching 
specific producers to specific consumers as different connections arent allowed 
to use the same ClientID at the same time....but it will be consistent in what 
goes where)

##########
File path: docs/user-manual/en/broker-balancers.md
##########
@@ -0,0 +1,176 @@
+# Broker Balancers
+Apache ActiveMQ Artemis broker balancers allow incoming client connections to 
be distributed across multiple [target brokers](target-brokers).
+The target brokers are grouped in [pools](#pools) and the broker balancers use 
a [target key](#target-key)
+to select a target broker from a pool of brokers according to a 
[policy](#policies).
+
+### This feature is still **EXPERIMENTAL** and not meant to be run in 
production yet. Furthermore, its configuration can change until declared as 
**officially stable**.
+
+## Target Broker
+Target broker is a broker that can accept incoming client connections and is 
local or remote.
+The local target is a special target that represents the same broker hosting 
the broker balancer.
+The remote target is another reachable broker.
+
+## Target Key
+The broker balancer uses a target key to select a target broker.
+It is a string retrieved from an incoming client connection, the supported 
values are:
+* `CLIENT_ID` is the JMS client ID;
+* `SNI_HOST` is the hostname indicated by the client in the SNI extension of 
the TLS protocol;
+* `SOURCE_IP` is the source IP address of the client;
+* `USER_NAME` is the username indicated by the client.
+
+## Pools
+The pool is a group of target brokers and checks periodically their state.
+It provides a list of ready target brokers to distribute incoming client 
connections only when it is active.
+A pool becomes active when the minimum number of ready target brokers defined 
by the `quorum-size` parameter is reached.
+When it is not active, it doesn't provide any target avoiding weird 
distribution at startup or after a restart.
+Including the local target broker in the pool allows broker hosting the broker 
balancer to accept incoming client connections. 
+By default, a pool doesn't include the local target broker, to include it the 
`local-target-enabled` parameter must be `true`.

Review comment:
       ```suggestion
   By default, a pool doesn't include the local broker, to include it as a 
target the `local-target-enabled` parameter must be `true`.
   ```
   Moving 'target' for clarity, as it isnt a 'target broker' yet if it isnt in 
the pool, which is whats explained next.

##########
File path: docs/user-manual/en/broker-balancers.md
##########
@@ -0,0 +1,176 @@
+# Broker Balancers
+Apache ActiveMQ Artemis broker balancers allow incoming client connections to 
be distributed across multiple [target brokers](target-brokers).
+The target brokers are grouped in [pools](#pools) and the broker balancers use 
a [target key](#target-key)
+to select a target broker from a pool of brokers according to a 
[policy](#policies).
+
+### This feature is still **EXPERIMENTAL** and not meant to be run in 
production yet. Furthermore, its configuration can change until declared as 
**officially stable**.
+
+## Target Broker
+Target broker is a broker that can accept incoming client connections and is 
local or remote.
+The local target is a special target that represents the same broker hosting 
the broker balancer.
+The remote target is another reachable broker.
+
+## Target Key
+The broker balancer uses a target key to select a target broker.
+It is a string retrieved from an incoming client connection, the supported 
values are:
+* `CLIENT_ID` is the JMS client ID;
+* `SNI_HOST` is the hostname indicated by the client in the SNI extension of 
the TLS protocol;
+* `SOURCE_IP` is the source IP address of the client;
+* `USER_NAME` is the username indicated by the client.
+
+## Pools
+The pool is a group of target brokers and checks periodically their state.
+It provides a list of ready target brokers to distribute incoming client 
connections only when it is active.
+A pool becomes active when the minimum number of ready target brokers defined 
by the `quorum-size` parameter is reached.
+When it is not active, it doesn't provide any target avoiding weird 
distribution at startup or after a restart.
+Including the local target broker in the pool allows broker hosting the broker 
balancer to accept incoming client connections. 
+By default, a pool doesn't include the local target broker, to include it the 
`local-target-enabled` parameter must be `true`.
+There are two pool types: [discovery pool](#discovery-pool) and [static 
pool](#static-pool).
+
+### Discovery Pool
+The discovery pool uses a [discovery group](clusters.md#discovery-groups) to 
discover the target brokers to add.
+Let's take a look at a discovery pool example from broker.xml that uses a 
discovery group:
+```xml
+<pool>
+    <discovery-group-ref discovery-group-name="dg1"/>
+</pool>
+```
+
+### Static Pool
+The static pool uses a list of static connectors to define the target brokers 
to add.
+Let's take a look at a static pool example from broker.xml that uses a list of 
static connectors:
+```xml
+<pool>
+    <static-connectors>
+        <connector-ref>connector1</connector-ref>
+        <connector-ref>connector2</connector-ref>
+        <connector-ref>connector3</connector-ref>
+    </static-connectors>
+</pool>
+```
+
+### Defining pools
+A pool is defined by the `pool` element that includes the following items:
+* the `username` element defines the username to connect to the target broker;
+* the `password` element defines the password to connect to the target broker;
+* the `check-period` element defines the often to check the target broker;
+* the `quorum-size` element defines the minimum number of ready targets to 
activate the pool;
+* the `quorum-timeout` element defines the timeout to get the minimum number 
of ready targets;
+* the `local-target-enabled` element defines whether the pool has to include a 
local target;
+* the `static-connectors` element defines a list of static connectors used by 
the [static pool](#static-pool);
+* the `discovery-group` element defines the [discovery 
group](clusters.md#discovery-groups) used by the [discovery 
pool](#discovery-pool).
+
+Let's take a look at a pool example from broker.xml:
+```xml
+<pool>
+    <quorum-size>2</quorum-size>
+    <check-period>1000</check-period>
+    <local-target-enabled>true</local-target-enabled>
+    <static-connectors>
+        <connector-ref>connector1</connector-ref>
+        <connector-ref>connector2</connector-ref>
+        <connector-ref>connector3</connector-ref>
+    </static-connectors>
+</pool>
+```
+
+## Policies
+The policy define how to select a broker from a pool. The included policies 
are:
+* `FIRST_ELEMENT` to select the first target broker from a pool. It is useful 
to select the ready target brokers
+  according to the priority defined with their sequence order, ie supposing to 
have 2 target brokers
+  this policy selects the second target broker only when the first target 
broker isn't ready.
+* `ROUND_ROBIN` to select a target sequentially from a pool, this policy is 
useful to evenly distribute;
+* `CONSISTENT_HASH` to select a target by a key. This policy always selects 
the same target broker for the same key until it is removed from the pool.
+* `LEAST_CONNECTIONS` to select the targets with the fewest active 
connections. This policy helps you maintain an equal distribution of active 
connections with the target brokers.
+
+A policy is defined by the `policy` element. Let's take a look at a policy 
example from broker.xml:
+```xml
+<policy name="FIRST_ELEMENT"/>
+```
+
+## Cache
+The broker balancer provides a cache with a timeout to improve the stickiness 
of the target broker selected.
+The broker balancer returns the same target broker for a target key as long as 
it is present in the cache and is ready.
+By default, the cache is enabled, to disable the cache the `cache-timeout` 
parameter must be `0`.
+
+## Defining broker balancers
+A broker balancer is defined by `broker-balancer` element, it includes the 
following items:
+* the `name` attribute defines the name of the broker balancer;
+* the `target-key` element defines what key to select a target broker, the 
supported values are: `SNI_HOST`, `SOURCE_IP`, `USER_NAME`.

Review comment:
       ```suggestion
   * the `target-key` element defines what key to select a target broker, the 
supported values are: `SNI_HOST`, `SOURCE_IP`, `USER_NAME`, `CLIENT_ID`.
   ```
   Missing CLIENT_ID
   

##########
File path: 
tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/balancing/TargetKeyTest.java
##########
@@ -0,0 +1,162 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.activemq.artemis.tests.integration.balancing;
+
+import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants;
+import 
org.apache.activemq.artemis.core.server.balancing.policies.FirstElementPolicy;
+import org.apache.activemq.artemis.core.server.balancing.policies.Policy;
+import 
org.apache.activemq.artemis.core.server.balancing.policies.PolicyFactory;
+import 
org.apache.activemq.artemis.core.server.balancing.policies.PolicyFactoryResolver;
+import org.apache.activemq.artemis.core.server.balancing.targets.Target;
+import org.apache.activemq.artemis.core.server.balancing.targets.TargetKey;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import javax.jms.Connection;
+import javax.jms.ConnectionFactory;
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+@RunWith(Parameterized.class)
+public class TargetKeyTest extends BalancingTestBase {
+
+   private static final String MOCK_POLICY_NAME = "MOCK_POLICY";
+
+   @Parameterized.Parameters(name = "protocol: {0}")
+   public static Collection<Object[]> data() {
+      Collection<Object[]> data = new ArrayList<>();
+
+      for (String protocol : Arrays.asList(new String[] {AMQP_PROTOCOL, 
CORE_PROTOCOL, OPENWIRE_PROTOCOL})) {
+         data.add(new Object[] {protocol});
+      }
+
+      return data;
+   }
+
+
+   private final String protocol;
+
+   private final List<String> keys = new ArrayList<>();
+
+
+   public TargetKeyTest(String protocol) {
+      this.protocol = protocol;
+   }
+
+   @Before
+   public void setup() throws Exception {
+      PolicyFactoryResolver.getInstance().registerPolicyFactory(
+         new PolicyFactory() {
+            @Override
+            public String[] getSupportedPolicies() {
+               return new String[] {MOCK_POLICY_NAME};
+            }
+
+            @Override
+            public Policy createPolicy(String policyName) {
+               return new FirstElementPolicy(MOCK_POLICY_NAME) {
+                  @Override
+                  public Target selectTarget(List<Target> targets, String key) 
{
+                     keys.add(key);
+                     return super.selectTarget(targets, key);
+                  }
+               };
+            }
+         });
+   }
+
+   @Test
+   public void testClientIDKey() throws Exception {
+      setupLiveServerWithDiscovery(0, GROUP_ADDRESS, GROUP_PORT, true, true, 
false);
+      setupBalancerServerWithDiscovery(0, TargetKey.CLIENT_ID, 
MOCK_POLICY_NAME, null, true, null, 1);
+      startServers(0);
+
+      ConnectionFactory connectionFactory = createFactory(protocol, false, 
TransportConstants.DEFAULT_HOST,
+         TransportConstants.DEFAULT_PORT + 0, "test", null, null);
+
+      keys.clear();
+
+      try (Connection connection = connectionFactory.createConnection()) {
+         connection.start();
+      }
+
+      Assert.assertEquals(1, keys.size());
+      Assert.assertEquals("test", keys.get(0));
+   }
+
+   @Test
+   public void testSNIHostKey() throws Exception {
+      setupLiveServerWithDiscovery(0, GROUP_ADDRESS, GROUP_PORT, true, true, 
false);
+      
getDefaultServerAcceptor(0).getParams().put(TransportConstants.SSL_ENABLED_PROP_NAME,
 true);
+      
getDefaultServerAcceptor(0).getParams().put(TransportConstants.KEYSTORE_PATH_PROP_NAME,
 "verified-localdomain-keystore.jks");

Review comment:
       The verified-localdomain-keystore.jks doesnt seem to have instructions 
for creation. Small tidyup to bring into place with your other recent PR?

##########
File path: examples/features/broker-balancer/symmetric-redirect/readme.md
##########
@@ -0,0 +1,5 @@
+# AMQP Broker Connection with Receivers

Review comment:
       Incorrect title

##########
File path: examples/features/broker-balancer/evenly-redirect/readme.md
##########
@@ -0,0 +1,6 @@
+# AMQP Broker Connection with Receivers

Review comment:
       Title needs fixed

##########
File path: examples/features/broker-balancer/pom.xml
##########
@@ -0,0 +1,62 @@
+<?xml version='1.0'?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/maven-v4_0_0.xsd";>
+   <modelVersion>4.0.0</modelVersion>
+
+   <parent>
+      <groupId>org.apache.activemq.examples.clustered</groupId>
+      <artifactId>broker-features</artifactId>
+      <version>2.18.0-SNAPSHOT</version>
+   </parent>
+
+   <groupId>org.apache.activemq.examples</groupId>
+   <artifactId>broker-balancer</artifactId>
+   <packaging>pom</packaging>
+   <name>ActiveMQ Artemis Broker Balancer Examples</name>
+
+   <!-- Properties -->
+   <properties>
+      <!--
+      Explicitly declaring the source encoding eliminates the following
+      message: [WARNING] Using platform encoding (UTF-8 actually) to copy
+      filtered resources, i.e. build is platform dependent!
+      -->
+      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

Review comment:
       Dont think this is needed, the apache and artemis parent poms both look 
to be setting it already. Certainly the comment can go.

##########
File path: 
examples/features/broker-balancer/symmetric-redirect/src/main/java/org/apache/activemq/artemis/jms/example/SymmetricRedirectExample.java
##########
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.artemis.jms.example;
+
+import javax.jms.Connection;
+import javax.jms.ConnectionFactory;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+
+import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory;
+
+/**
+ * This example is demonstrating how incoming client connections are evely 
redirected from one broker towards
+ * worker brokers.

Review comment:
       Doesnt seem to match with the readme.

##########
File path: 
examples/features/broker-balancer/symmetric-redirect/src/main/java/org/apache/activemq/artemis/jms/example/SymmetricRedirectExample.java
##########
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.artemis.jms.example;
+
+import javax.jms.Connection;
+import javax.jms.ConnectionFactory;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+
+import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory;
+
+/**
+ * This example is demonstrating how incoming client connections are evely 
redirected from one broker towards
+ * worker brokers.
+ */
+public class SymmetricRedirectExample {
+
+   public static void main(final String[] args) throws Exception {
+
+      ConnectionFactory connectionFactoryClient0Server0 = new 
ActiveMQConnectionFactory("tcp://localhost:61616?ha=true&reconnectAttempts=30&clientID=CLIENT_0");
+      ConnectionFactory connectionFactoryClient1Server0 = new 
ActiveMQConnectionFactory("tcp://localhost:61616?ha=true&reconnectAttempts=30&clientID=CLIENT_1");
+      ConnectionFactory connectionFactoryClient0Server1 = new 
ActiveMQConnectionFactory("tcp://localhost:61617?ha=true&reconnectAttempts=30&clientID=CLIENT_0");
+      ConnectionFactory connectionFactoryClient1Server1 = new 
ActiveMQConnectionFactory("tcp://localhost:61617?ha=true&reconnectAttempts=30&clientID=CLIENT_1");
+
+      /**
+       * Step 1. Create a connection for producer0 and producer1, and send a 
few messages.
+       * the server0 will redirect the connection of each producer to a 
different target brokers.
+       */
+      Connection connectionProducer0 = null;
+      Connection connectionProducer1 = null;
+
+      try {
+         connectionProducer0 = 
connectionFactoryClient0Server0.createConnection();
+         connectionProducer1 = 
connectionFactoryClient1Server0.createConnection();
+
+         for (Connection connectionProducer : new Connection[] 
{connectionProducer0, connectionProducer1}) {
+            Session session = connectionProducer.createSession(false, 
Session.AUTO_ACKNOWLEDGE);
+
+            Queue queue = session.createQueue("exampleQueue" + 
connectionProducer.getClientID());
+            MessageProducer sender = session.createProducer(queue);
+            for (int i = 0; i < 100; i++) {
+               TextMessage message = session.createTextMessage("Hello world n" 
+ i + " - " + connectionProducer.getClientID());
+               System.out.println("Sending message " + message.getText() + "/" 
+ connectionProducer.getClientID());
+               sender.send(message);
+            }
+         }
+      } finally {
+         if (connectionProducer0 != null) {
+            connectionProducer0.close();
+         }
+
+         if (connectionProducer1 != null) {
+            connectionProducer1.close();
+         }
+      }
+
+      /**
+       * Step 2. create a connection for consumer0 and consumer1, and receive 
a few messages.
+       * the server0 will redirect the connection to the same target broker of 
the respective producer
+       * because the consumer and the producer share the same clientID.

Review comment:
       ```suggestion
          * the server1 will redirect the connection to the same target broker 
of the respective producer
          * from earlier as the new consumer connection uses the same ClientID.
   ```
   The server is wrong, they connect to server 1 this time.
   
   Avoiding saying 'share the same ClientID' since only one connection at a 
time can use a ClientID (so a real application probably couldnt use this 
precise example setup of matching-clientid, but some other setup...unless they 
could coordinate when the producer and consumer connections would be around, at 
which point they dont really need a broker)

##########
File path: docs/user-manual/en/broker-balancers.md
##########
@@ -0,0 +1,176 @@
+# Broker Balancers
+Apache ActiveMQ Artemis broker balancers allow incoming client connections to 
be distributed across multiple [target brokers](target-brokers).
+The target brokers are grouped in [pools](#pools) and the broker balancers use 
a [target key](#target-key)
+to select a target broker from a pool of brokers according to a 
[policy](#policies).
+
+### This feature is still **EXPERIMENTAL** and not meant to be run in 
production yet. Furthermore, its configuration can change until declared as 
**officially stable**.
+
+## Target Broker
+Target broker is a broker that can accept incoming client connections and is 
local or remote.
+The local target is a special target that represents the same broker hosting 
the broker balancer.
+The remote target is another reachable broker.
+
+## Target Key
+The broker balancer uses a target key to select a target broker.
+It is a string retrieved from an incoming client connection, the supported 
values are:
+* `CLIENT_ID` is the JMS client ID;
+* `SNI_HOST` is the hostname indicated by the client in the SNI extension of 
the TLS protocol;
+* `SOURCE_IP` is the source IP address of the client;
+* `USER_NAME` is the username indicated by the client.
+
+## Pools
+The pool is a group of target brokers and checks periodically their state.
+It provides a list of ready target brokers to distribute incoming client 
connections only when it is active.
+A pool becomes active when the minimum number of ready target brokers defined 
by the `quorum-size` parameter is reached.
+When it is not active, it doesn't provide any target avoiding weird 
distribution at startup or after a restart.
+Including the local target broker in the pool allows broker hosting the broker 
balancer to accept incoming client connections. 
+By default, a pool doesn't include the local target broker, to include it the 
`local-target-enabled` parameter must be `true`.
+There are two pool types: [discovery pool](#discovery-pool) and [static 
pool](#static-pool).
+
+### Discovery Pool
+The discovery pool uses a [discovery group](clusters.md#discovery-groups) to 
discover the target brokers to add.
+Let's take a look at a discovery pool example from broker.xml that uses a 
discovery group:
+```xml
+<pool>
+    <discovery-group-ref discovery-group-name="dg1"/>
+</pool>
+```
+
+### Static Pool
+The static pool uses a list of static connectors to define the target brokers 
to add.
+Let's take a look at a static pool example from broker.xml that uses a list of 
static connectors:
+```xml
+<pool>
+    <static-connectors>
+        <connector-ref>connector1</connector-ref>
+        <connector-ref>connector2</connector-ref>
+        <connector-ref>connector3</connector-ref>
+    </static-connectors>
+</pool>
+```
+
+### Defining pools
+A pool is defined by the `pool` element that includes the following items:
+* the `username` element defines the username to connect to the target broker;
+* the `password` element defines the password to connect to the target broker;
+* the `check-period` element defines the often to check the target broker;
+* the `quorum-size` element defines the minimum number of ready targets to 
activate the pool;
+* the `quorum-timeout` element defines the timeout to get the minimum number 
of ready targets;
+* the `local-target-enabled` element defines whether the pool has to include a 
local target;
+* the `static-connectors` element defines a list of static connectors used by 
the [static pool](#static-pool);
+* the `discovery-group` element defines the [discovery 
group](clusters.md#discovery-groups) used by the [discovery 
pool](#discovery-pool).
+
+Let's take a look at a pool example from broker.xml:
+```xml
+<pool>
+    <quorum-size>2</quorum-size>
+    <check-period>1000</check-period>
+    <local-target-enabled>true</local-target-enabled>
+    <static-connectors>
+        <connector-ref>connector1</connector-ref>
+        <connector-ref>connector2</connector-ref>
+        <connector-ref>connector3</connector-ref>
+    </static-connectors>
+</pool>
+```
+
+## Policies
+The policy define how to select a broker from a pool. The included policies 
are:
+* `FIRST_ELEMENT` to select the first target broker from a pool. It is useful 
to select the ready target brokers
+  according to the priority defined with their sequence order, ie supposing to 
have 2 target brokers
+  this policy selects the second target broker only when the first target 
broker isn't ready.

Review comment:
       ```suggestion
   * `FIRST_ELEMENT` to select the first target broker from the pool which is 
ready. It is useful to select the ready target brokers
     according to the priority defined with their sequence order, ie supposing 
there are 2 target brokers
     this policy selects the second target broker only when the first target 
broker isn't ready.
   ```

##########
File path: 
examples/features/broker-balancer/evenly-redirect/src/main/java/org/apache/activemq/artemis/jms/example/EvenlyRedirectExample.java
##########
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.artemis.jms.example;
+
+import javax.jms.Connection;
+import javax.jms.ConnectionFactory;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+
+import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory;
+
+/**
+ * This example is demonstrating how incoming client connections are evely 
redirected from one broker towards
+ * worker brokers.
+ */
+public class EvenlyRedirectExample {
+
+   public static void main(final String[] args) throws Exception {
+
+      ConnectionFactory connectionFactoryClient0 = new 
ActiveMQConnectionFactory("tcp://localhost:61616?ha=true&reconnectAttempts=30&clientID=CLIENT_0");
+      ConnectionFactory connectionFactoryClient1 = new 
ActiveMQConnectionFactory("tcp://localhost:61616?ha=true&reconnectAttempts=30&clientID=CLIENT_1");
+
+      /**
+       * Step 1. Create a connection for producer0 and producer1, and send a 
few messages.
+       * the server0 will redirect the connection of each producer to a 
different target brokers.
+       */
+      Connection connectionProducer0 = null;
+      Connection connectionProducer1 = null;
+
+      try {
+         connectionProducer0 = connectionFactoryClient0.createConnection();
+         connectionProducer1 = connectionFactoryClient1.createConnection();
+
+         for (Connection connectionProducer : new Connection[] 
{connectionProducer0, connectionProducer1}) {
+            Session session = connectionProducer.createSession(false, 
Session.AUTO_ACKNOWLEDGE);
+
+            Queue queue = session.createQueue("exampleQueue");
+            MessageProducer sender = session.createProducer(queue);
+            for (int i = 0; i < 100; i++) {
+               sender.send(session.createTextMessage("Hello world n" + i + " - 
" + connectionProducer.getClientID()));
+            }
+         }
+      } finally {
+         if (connectionProducer0 != null) {
+            connectionProducer0.close();
+         }
+
+         if (connectionProducer1 != null) {
+            connectionProducer1.close();
+         }
+      }
+
+      /**
+       * Step 2. create a connection for consumer0 and consumer1, and receive 
a few messages.
+       * the server0 will redirect the connection to the same target broker of 
the respective producer
+       * because the consumer and the producer share the same clientID.

Review comment:
       Actually, as this uses 2 different ClientIDs but is doing 'least 
connections', is the ClientID relevant? When the second set of connections 
happens...what guarantee is it that they go to the same place as the producer 
if it is only doing 'least connections' balance and both servers have 0? Would 
it work as described if I changed the order of connection creation on the 
second round?

##########
File path: 
examples/features/broker-balancer/evenly-redirect/src/main/java/org/apache/activemq/artemis/jms/example/EvenlyRedirectExample.java
##########
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.artemis.jms.example;
+
+import javax.jms.Connection;
+import javax.jms.ConnectionFactory;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+
+import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory;
+
+/**
+ * This example is demonstrating how incoming client connections are evely 
redirected from one broker towards
+ * worker brokers.
+ */
+public class EvenlyRedirectExample {
+
+   public static void main(final String[] args) throws Exception {
+
+      ConnectionFactory connectionFactoryClient0 = new 
ActiveMQConnectionFactory("tcp://localhost:61616?ha=true&reconnectAttempts=30&clientID=CLIENT_0");
+      ConnectionFactory connectionFactoryClient1 = new 
ActiveMQConnectionFactory("tcp://localhost:61616?ha=true&reconnectAttempts=30&clientID=CLIENT_1");
+
+      /**
+       * Step 1. Create a connection for producer0 and producer1, and send a 
few messages.
+       * the server0 will redirect the connection of each producer to a 
different target brokers.
+       */
+      Connection connectionProducer0 = null;
+      Connection connectionProducer1 = null;
+
+      try {
+         connectionProducer0 = connectionFactoryClient0.createConnection();
+         connectionProducer1 = connectionFactoryClient1.createConnection();
+
+         for (Connection connectionProducer : new Connection[] 
{connectionProducer0, connectionProducer1}) {
+            Session session = connectionProducer.createSession(false, 
Session.AUTO_ACKNOWLEDGE);
+
+            Queue queue = session.createQueue("exampleQueue");
+            MessageProducer sender = session.createProducer(queue);
+            for (int i = 0; i < 100; i++) {
+               sender.send(session.createTextMessage("Hello world n" + i + " - 
" + connectionProducer.getClientID()));
+            }
+         }
+      } finally {
+         if (connectionProducer0 != null) {
+            connectionProducer0.close();
+         }
+
+         if (connectionProducer1 != null) {
+            connectionProducer1.close();
+         }
+      }
+
+      /**
+       * Step 2. create a connection for consumer0 and consumer1, and receive 
a few messages.
+       * the server0 will redirect the connection to the same target broker of 
the respective producer
+       * because the consumer and the producer share the same clientID.

Review comment:
       ```suggestion
          * the server0 will redirect the connection to the same target broker 
of the respective producer
          * from earlier as the new consumer connection uses the same ClientID.
   ```
   
   Avoiding saying 'share the same ClientID' since only one connection at a 
time can use a ClientID 

##########
File path: 
tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/balancing/TargetKeyTest.java
##########
@@ -0,0 +1,162 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.activemq.artemis.tests.integration.balancing;
+
+import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants;
+import 
org.apache.activemq.artemis.core.server.balancing.policies.FirstElementPolicy;
+import org.apache.activemq.artemis.core.server.balancing.policies.Policy;
+import 
org.apache.activemq.artemis.core.server.balancing.policies.PolicyFactory;
+import 
org.apache.activemq.artemis.core.server.balancing.policies.PolicyFactoryResolver;
+import org.apache.activemq.artemis.core.server.balancing.targets.Target;
+import org.apache.activemq.artemis.core.server.balancing.targets.TargetKey;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import javax.jms.Connection;
+import javax.jms.ConnectionFactory;
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+@RunWith(Parameterized.class)
+public class TargetKeyTest extends BalancingTestBase {
+
+   private static final String MOCK_POLICY_NAME = "MOCK_POLICY";
+
+   @Parameterized.Parameters(name = "protocol: {0}")
+   public static Collection<Object[]> data() {
+      Collection<Object[]> data = new ArrayList<>();
+
+      for (String protocol : Arrays.asList(new String[] {AMQP_PROTOCOL, 
CORE_PROTOCOL, OPENWIRE_PROTOCOL})) {
+         data.add(new Object[] {protocol});
+      }
+
+      return data;
+   }
+
+
+   private final String protocol;
+
+   private final List<String> keys = new ArrayList<>();
+
+
+   public TargetKeyTest(String protocol) {
+      this.protocol = protocol;
+   }
+
+   @Before
+   public void setup() throws Exception {
+      PolicyFactoryResolver.getInstance().registerPolicyFactory(
+         new PolicyFactory() {
+            @Override
+            public String[] getSupportedPolicies() {
+               return new String[] {MOCK_POLICY_NAME};
+            }
+
+            @Override
+            public Policy createPolicy(String policyName) {
+               return new FirstElementPolicy(MOCK_POLICY_NAME) {
+                  @Override
+                  public Target selectTarget(List<Target> targets, String key) 
{
+                     keys.add(key);
+                     return super.selectTarget(targets, key);
+                  }
+               };
+            }
+         });
+   }
+
+   @Test
+   public void testClientIDKey() throws Exception {
+      setupLiveServerWithDiscovery(0, GROUP_ADDRESS, GROUP_PORT, true, true, 
false);
+      setupBalancerServerWithDiscovery(0, TargetKey.CLIENT_ID, 
MOCK_POLICY_NAME, null, true, null, 1);
+      startServers(0);
+
+      ConnectionFactory connectionFactory = createFactory(protocol, false, 
TransportConstants.DEFAULT_HOST,
+         TransportConstants.DEFAULT_PORT + 0, "test", null, null);
+
+      keys.clear();
+
+      try (Connection connection = connectionFactory.createConnection()) {
+         connection.start();
+      }
+
+      Assert.assertEquals(1, keys.size());
+      Assert.assertEquals("test", keys.get(0));
+   }
+
+   @Test
+   public void testSNIHostKey() throws Exception {
+      setupLiveServerWithDiscovery(0, GROUP_ADDRESS, GROUP_PORT, true, true, 
false);
+      
getDefaultServerAcceptor(0).getParams().put(TransportConstants.SSL_ENABLED_PROP_NAME,
 true);
+      
getDefaultServerAcceptor(0).getParams().put(TransportConstants.KEYSTORE_PATH_PROP_NAME,
 "verified-localdomain-keystore.jks");
+      
getDefaultServerAcceptor(0).getParams().put(TransportConstants.KEYSTORE_PASSWORD_PROP_NAME,
 "secureexample");
+
+      setupBalancerServerWithDiscovery(0, TargetKey.SNI_HOST, 
MOCK_POLICY_NAME, null, true, null, 1);
+      startServers(0);
+
+      ConnectionFactory connectionFactory = createFactory(protocol, true, 
"localhost.localdomain",

Review comment:
       You cant for Qpid JMS, dont know for openwire.

##########
File path: 
examples/features/broker-balancer/evenly-redirect/src/main/java/org/apache/activemq/artemis/jms/example/EvenlyRedirectExample.java
##########
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.artemis.jms.example;
+
+import javax.jms.Connection;
+import javax.jms.ConnectionFactory;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+
+import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory;
+
+/**
+ * This example is demonstrating how incoming client connections are evely 
redirected from one broker towards
+ * worker brokers.
+ */
+public class EvenlyRedirectExample {
+
+   public static void main(final String[] args) throws Exception {
+
+      ConnectionFactory connectionFactoryClient0 = new 
ActiveMQConnectionFactory("tcp://localhost:61616?ha=true&reconnectAttempts=30&clientID=CLIENT_0");
+      ConnectionFactory connectionFactoryClient1 = new 
ActiveMQConnectionFactory("tcp://localhost:61616?ha=true&reconnectAttempts=30&clientID=CLIENT_1");
+
+      /**
+       * Step 1. Create a connection for producer0 and producer1, and send a 
few messages.
+       * the server0 will redirect the connection of each producer to a 
different target brokers.
+       */
+      Connection connectionProducer0 = null;
+      Connection connectionProducer1 = null;
+
+      try {
+         connectionProducer0 = connectionFactoryClient0.createConnection();
+         connectionProducer1 = connectionFactoryClient1.createConnection();
+
+         for (Connection connectionProducer : new Connection[] 
{connectionProducer0, connectionProducer1}) {
+            Session session = connectionProducer.createSession(false, 
Session.AUTO_ACKNOWLEDGE);
+
+            Queue queue = session.createQueue("exampleQueue");
+            MessageProducer sender = session.createProducer(queue);
+            for (int i = 0; i < 100; i++) {
+               sender.send(session.createTextMessage("Hello world n" + i + " - 
" + connectionProducer.getClientID()));
+            }
+         }
+      } finally {
+         if (connectionProducer0 != null) {
+            connectionProducer0.close();
+         }
+
+         if (connectionProducer1 != null) {
+            connectionProducer1.close();
+         }
+      }
+
+      /**
+       * Step 2. create a connection for consumer0 and consumer1, and receive 
a few messages.
+       * the server0 will redirect the connection to the same target broker of 
the respective producer
+       * because the consumer and the producer share the same clientID.

Review comment:
       Oh I see, it caches the results and sends things to the same place next 
time, even if that isnt the one with least connections? That isnt at all what I 
had expected from it on initial reading (I thought the clientid would only be 
getting used for the defined local target filter in that case).
   
   Its unclear to me where any of it is looking at ClientID prefixes, seems 
more like it looking at the whole value, where does the prefix consideration 
happen? (E.g would looking at only a prefix need to use the filtering stuff? Or 
is there a specific syntax to use?) The example seems to use ones that are 
exact matches across the sets of connections. 

##########
File path: docs/user-manual/en/broker-balancers.md
##########
@@ -0,0 +1,176 @@
+# Broker Balancers
+Apache ActiveMQ Artemis broker balancers allow incoming client connections to 
be distributed across multiple [target brokers](target-brokers).
+The target brokers are grouped in [pools](#pools) and the broker balancers use 
a [target key](#target-key)
+to select a target broker from a pool of brokers according to a 
[policy](#policies).
+
+### This feature is still **EXPERIMENTAL** and not meant to be run in 
production yet. Furthermore, its configuration can change until declared as 
**officially stable**.
+
+## Target Broker
+Target broker is a broker that can accept incoming client connections and is 
local or remote.
+The local target is a special target that represents the same broker hosting 
the broker balancer.
+The remote target is another reachable broker.
+
+## Target Key
+The broker balancer uses a target key to select a target broker.
+It is a string retrieved from an incoming client connection, the supported 
values are:
+* `CLIENT_ID` is the JMS client ID;
+* `SNI_HOST` is the hostname indicated by the client in the SNI extension of 
the TLS protocol;
+* `SOURCE_IP` is the source IP address of the client;
+* `USER_NAME` is the username indicated by the client.
+
+## Pools
+The pool is a group of target brokers and checks periodically their state.
+It provides a list of ready target brokers to distribute incoming client 
connections only when it is active.
+A pool becomes active when the minimum number of ready target brokers defined 
by the `quorum-size` parameter is reached.
+When it is not active, it doesn't provide any target avoiding weird 
distribution at startup or after a restart.
+Including the local target broker in the pool allows broker hosting the broker 
balancer to accept incoming client connections.

Review comment:
       ```suggestion
   Including the local broker in the target pool allows broker hosting the 
balancer to accept incoming client connections as well.
   ```
   Similar change for 'target' as before (not sure how I missed it first time). 
Also drops the 'broker' before 'balancer' as the context is already extremely 
clear already and there are 3 uses of 'broker' within 9 words which is 
excessive.

##########
File path: examples/features/broker-balancer/evenly-redirect/readme.md
##########
@@ -0,0 +1,6 @@
+# Evenly Redirect Example
+
+To run the example, simply type **mvn verify** from this directory, or **mvn 
-PnoServer verify** if you want to create and start the broker manually.
+
+This example demonstrates how incoming client connections are evenly 
redirected across two brokers
+using a third broker with a broker balancer to redirect incoming client 
connections.

Review comment:
       ```suggestion
   using a third broker as a balancer to redirect incoming client connections, 
based on a least-connections policy and caching on a filtered prefix of the 
connection ClientID.
   ```
   or 
   ```suggestion
   using a third broker with a balancer to redirect incoming client 
connections, based on a least-connections policy and caching on a filtered 
prefix of the connection ClientID.
   ```
   Expand on how it balances, and that there is caching involved. Drops a 
'broker' mention to very slightly ease the presence overload.
   
   Possibly also worth noting that this isnt particularly suitable for Durable 
Subscription usage as the selected target could change after restart for 
example?

##########
File path: 
examples/features/broker-balancer/evenly-redirect/src/main/java/org/apache/activemq/artemis/jms/example/EvenlyRedirectExample.java
##########
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.artemis.jms.example;
+
+import javax.jms.Connection;
+import javax.jms.ConnectionFactory;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+
+import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory;
+
+/**
+ * This example demonstrates how incoming client connections are evenly 
redirected across two brokers
+ * using a third broker with a broker balancer to redirect incoming client 
connections.
+ */
+public class EvenlyRedirectExample {
+
+   public static void main(final String[] args) throws Exception {
+
+      /**
+       * Step 1. Create a connection for producer0 and producer1, and send a 
few messages.
+       * the server0 will redirect the connection of each producer to a 
different target brokers.
+       */
+      ConnectionFactory connectionFactoryProducer0 = new 
ActiveMQConnectionFactory("tcp://localhost:61616?ha=true&reconnectAttempts=30&clientID=FOO_PRODUCER");
+      ConnectionFactory connectionFactoryProducer1 = new 
ActiveMQConnectionFactory("tcp://localhost:61616?ha=true&reconnectAttempts=30&clientID=TOO_PRODUCER");

Review comment:
       ```suggestion
          * Step 1. Create a connection for producer0 and producer1, and send a 
few messages.
          * the server0 will redirect the connection of each producer to a 
different target broker.
          */
         ConnectionFactory connectionFactoryProducer0 = new 
ActiveMQConnectionFactory("tcp://localhost:61616?ha=true&reconnectAttempts=30&clientID=FOO_PRODUCER");
         ConnectionFactory connectionFactoryProducer1 = new 
ActiveMQConnectionFactory("tcp://localhost:61616?ha=true&reconnectAttempts=30&clientID=BAR_PRODUCER");
   ```
   FOO and TOO are pretty close and dont jump out as being different as easily 
as using something further afield like BAR would. Making it do so helps makes 
the example more obvious.

##########
File path: 
examples/features/broker-balancer/evenly-redirect/src/main/java/org/apache/activemq/artemis/jms/example/EvenlyRedirectExample.java
##########
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.artemis.jms.example;
+
+import javax.jms.Connection;
+import javax.jms.ConnectionFactory;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+
+import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory;
+
+/**
+ * This example demonstrates how incoming client connections are evenly 
redirected across two brokers
+ * using a third broker with a broker balancer to redirect incoming client 
connections.
+ */
+public class EvenlyRedirectExample {
+
+   public static void main(final String[] args) throws Exception {
+
+      /**
+       * Step 1. Create a connection for producer0 and producer1, and send a 
few messages.
+       * the server0 will redirect the connection of each producer to a 
different target brokers.
+       */
+      ConnectionFactory connectionFactoryProducer0 = new 
ActiveMQConnectionFactory("tcp://localhost:61616?ha=true&reconnectAttempts=30&clientID=FOO_PRODUCER");
+      ConnectionFactory connectionFactoryProducer1 = new 
ActiveMQConnectionFactory("tcp://localhost:61616?ha=true&reconnectAttempts=30&clientID=TOO_PRODUCER");
+
+      Connection connectionProducer0 = null;
+      Connection connectionProducer1 = null;
+
+      try {
+         connectionProducer0 = connectionFactoryProducer0.createConnection();
+         connectionProducer1 = connectionFactoryProducer1.createConnection();
+
+         for (Connection connectionProducer : new Connection[] 
{connectionProducer0, connectionProducer1}) {
+            Session session = connectionProducer.createSession(false, 
Session.AUTO_ACKNOWLEDGE);
+
+            Queue queue = session.createQueue("exampleQueue");
+            MessageProducer sender = session.createProducer(queue);
+            for (int i = 0; i < 100; i++) {
+               sender.send(session.createTextMessage("Hello world n" + i + " - 
" + connectionProducer.getClientID()));
+            }
+         }
+      } finally {
+         if (connectionProducer0 != null) {
+            connectionProducer0.close();
+         }
+
+         if (connectionProducer1 != null) {
+            connectionProducer1.close();
+         }
+      }
+
+      /**
+       * Step 2. create a connection for consumer0 and consumer1, and receive 
a few messages.
+       * the server0 will redirect the connection to the same target broker of 
the respective producer
+       * because the consumer and the producer share the same clientID prefix.

Review comment:
       ```suggestion
          * because the consumer and the producer connections have the same 
clientID prefix, which
          * the balancer configuration filters the target key on and caches the 
target broker the policy selects.
   ```
   Expand on how it is used and why it matters

##########
File path: docs/user-manual/en/broker-balancers.md
##########
@@ -0,0 +1,176 @@
+# Broker Balancers
+Apache ActiveMQ Artemis broker balancers allow incoming client connections to 
be distributed across multiple [target brokers](target-brokers).
+The target brokers are grouped in [pools](#pools) and the broker balancers use 
a [target key](#target-key)
+to select a target broker from a pool of brokers according to a 
[policy](#policies).
+
+### This feature is still **EXPERIMENTAL** and not meant to be run in 
production yet. Furthermore, its configuration can change until declared as 
**officially stable**.
+
+## Target Broker
+Target broker is a broker that can accept incoming client connections and is 
local or remote.
+The local target is a special target that represents the same broker hosting 
the broker balancer.
+The remote target is another reachable broker.
+
+## Target Key
+The broker balancer uses a target key to select a target broker.
+It is a string retrieved from an incoming client connection, the supported 
values are:
+* `CLIENT_ID` is the JMS client ID;
+* `SNI_HOST` is the hostname indicated by the client in the SNI extension of 
the TLS protocol;
+* `SOURCE_IP` is the source IP address of the client;
+* `USER_NAME` is the username indicated by the client.
+
+## Pools
+The pool is a group of target brokers and checks periodically their state.
+It provides a list of ready target brokers to distribute incoming client 
connections only when it is active.
+A pool becomes active when the minimum number of ready target brokers defined 
by the `quorum-size` parameter is reached.
+When it is not active, it doesn't provide any target avoiding weird 
distribution at startup or after a restart.
+Including the local target broker in the pool allows broker hosting the broker 
balancer to accept incoming client connections.
+By default, a pool doesn't include the local broker, to include it as a target 
the `local-target-enabled` parameter must be `true`.
+There are two pool types: [discovery pool](#discovery-pool) and [static 
pool](#static-pool).
+
+### Discovery Pool
+The discovery pool uses a [discovery group](clusters.md#discovery-groups) to 
discover the target brokers to add.
+Let's take a look at a discovery pool example from broker.xml that uses a 
discovery group:
+```xml
+<pool>
+    <discovery-group-ref discovery-group-name="dg1"/>
+</pool>
+```
+
+### Static Pool
+The static pool uses a list of static connectors to define the target brokers 
to add.
+Let's take a look at a static pool example from broker.xml that uses a list of 
static connectors:
+```xml
+<pool>
+    <static-connectors>
+        <connector-ref>connector1</connector-ref>
+        <connector-ref>connector2</connector-ref>
+        <connector-ref>connector3</connector-ref>
+    </static-connectors>
+</pool>
+```
+
+### Defining pools
+A pool is defined by the `pool` element that includes the following items:
+* the `username` element defines the username to connect to the target broker;
+* the `password` element defines the password to connect to the target broker;
+* the `check-period` element defines the often to check the target broker;
+* the `quorum-size` element defines the minimum number of ready targets to 
activate the pool;
+* the `quorum-timeout` element defines the timeout to get the minimum number 
of ready targets;
+* the `local-target-enabled` element defines whether the pool has to include a 
local target;
+* the `static-connectors` element defines a list of static connectors used by 
the [static pool](#static-pool);
+* the `discovery-group` element defines the [discovery 
group](clusters.md#discovery-groups) used by the [discovery 
pool](#discovery-pool).
+
+Let's take a look at a pool example from broker.xml:
+```xml
+<pool>
+    <quorum-size>2</quorum-size>
+    <check-period>1000</check-period>
+    <local-target-enabled>true</local-target-enabled>
+    <static-connectors>
+        <connector-ref>connector1</connector-ref>
+        <connector-ref>connector2</connector-ref>
+        <connector-ref>connector3</connector-ref>
+    </static-connectors>
+</pool>
+```
+
+## Policies
+The policy define how to select a broker from a pool. The included policies 
are:
+* `FIRST_ELEMENT` to select the first target broker from the pool which is 
ready. It is useful to select the ready target brokers
+  according to the priority defined with their sequence order, ie supposing 
there are 2 target brokers
+  this policy selects the second target broker only when the first target 
broker isn't ready.
+* `ROUND_ROBIN` to select a target sequentially from a pool, this policy is 
useful to evenly distribute;
+* `CONSISTENT_HASH` to select a target by a key. This policy always selects 
the same target broker for the same key until it is removed from the pool.
+* `LEAST_CONNECTIONS` to select the targets with the fewest active 
connections. This policy helps you maintain an equal distribution of active 
connections with the target brokers.
+
+A policy is defined by the `policy` element. Let's take a look at a policy 
example from broker.xml:
+```xml
+<policy name="FIRST_ELEMENT"/>
+```
+
+## Cache
+The broker balancer provides a cache with a timeout to improve the stickiness 
of the target broker selected.
+The broker balancer returns the same target broker for a target key as long as 
it is present in the cache and is ready.
+By default, the cache is enabled, to disable the cache the `cache-timeout` 
parameter must be `0`.
+
+## Defining broker balancers
+A broker balancer is defined by `broker-balancer` element, it includes the 
following items:
+* the `name` attribute defines the name of the broker balancer;
+* the `target-key` element defines what key to select a target broker, the 
supported values are: `CLIENT_ID`, `SNI_HOST`, `SOURCE_IP`, `USER_NAME`.
+* the `target-key-filter` element defines a regular expression to filter the 
resolved keys;
+* the `local-target-filter` element defines a regular expression to filter the 
keys that have to return a local target;
+* the `cache-timeout` element is the time period for a target broker to remain 
in the cache;
+* the `pool` element defines the pool to group the target brokers, see 
[pools](#pools).
+* the `policy` element defines the policy used to select the target brokers, 
see [policies](#policies);
+
+Let's take a look at some broker balancer examples from broker.xml:
+```xml
+<broker-balancers>
+    <broker-balancer name="simple-balancer">
+        <policy name="FIRST_ELEMENT"/>
+        <pool>
+            <static-connectors>
+                <connector-ref>connector1</connector-ref>
+                <connector-ref>connector2</connector-ref>
+                <connector-ref>connector3</connector-ref>
+            </static-connectors>
+        </pool>
+    </broker-balancer>
+    <broker-balancer name="consistent-hash-balancer">
+        <target-key>USER_NAME</target-key>
+        <local-filter>admin</local-filter>
+        <policy name="CONSISTENT_HASH"/>
+        <pool>
+            <local-target-enabled>true</local-target-enabled>
+            <discovery-group-ref discovery-group-name="dg1"/>
+        </pool>
+    <policy name="CONSISTENT_HASH"/>
+    </broker-balancer>
+    <broker-balancer name="least-connections-balancer">
+        <policy name="LEAST_CONNECTIONS"/>
+        <pool>
+            <discovery-group-ref discovery-group-name="dg2"/>
+        </pool>
+    </broker-balancer>
+</broker-balancers>

Review comment:
       No example of CLIENTID target key use. I think its important to talk 
about the 'filtering' usage along with that, which will likely be critical to 
many use cases.

##########
File path: docs/user-manual/en/broker-balancers.md
##########
@@ -0,0 +1,176 @@
+# Broker Balancers
+Apache ActiveMQ Artemis broker balancers allow incoming client connections to 
be distributed across multiple [target brokers](target-brokers).
+The target brokers are grouped in [pools](#pools) and the broker balancers use 
a [target key](#target-key)
+to select a target broker from a pool of brokers according to a 
[policy](#policies).
+
+### This feature is still **EXPERIMENTAL** and not meant to be run in 
production yet. Furthermore, its configuration can change until declared as 
**officially stable**.
+
+## Target Broker
+Target broker is a broker that can accept incoming client connections and is 
local or remote.
+The local target is a special target that represents the same broker hosting 
the broker balancer.
+The remote target is another reachable broker.
+
+## Target Key
+The broker balancer uses a target key to select a target broker.
+It is a string retrieved from an incoming client connection, the supported 
values are:
+* `CLIENT_ID` is the JMS client ID;
+* `SNI_HOST` is the hostname indicated by the client in the SNI extension of 
the TLS protocol;
+* `SOURCE_IP` is the source IP address of the client;
+* `USER_NAME` is the username indicated by the client.
+
+## Pools
+The pool is a group of target brokers and checks periodically their state.
+It provides a list of ready target brokers to distribute incoming client 
connections only when it is active.
+A pool becomes active when the minimum number of ready target brokers defined 
by the `quorum-size` parameter is reached.
+When it is not active, it doesn't provide any target avoiding weird 
distribution at startup or after a restart.
+Including the local target broker in the pool allows broker hosting the broker 
balancer to accept incoming client connections.
+By default, a pool doesn't include the local broker, to include it as a target 
the `local-target-enabled` parameter must be `true`.
+There are two pool types: [discovery pool](#discovery-pool) and [static 
pool](#static-pool).
+
+### Discovery Pool
+The discovery pool uses a [discovery group](clusters.md#discovery-groups) to 
discover the target brokers to add.
+Let's take a look at a discovery pool example from broker.xml that uses a 
discovery group:
+```xml
+<pool>
+    <discovery-group-ref discovery-group-name="dg1"/>
+</pool>
+```
+
+### Static Pool
+The static pool uses a list of static connectors to define the target brokers 
to add.
+Let's take a look at a static pool example from broker.xml that uses a list of 
static connectors:
+```xml
+<pool>
+    <static-connectors>
+        <connector-ref>connector1</connector-ref>
+        <connector-ref>connector2</connector-ref>
+        <connector-ref>connector3</connector-ref>
+    </static-connectors>
+</pool>
+```
+
+### Defining pools
+A pool is defined by the `pool` element that includes the following items:
+* the `username` element defines the username to connect to the target broker;
+* the `password` element defines the password to connect to the target broker;
+* the `check-period` element defines the often to check the target broker;
+* the `quorum-size` element defines the minimum number of ready targets to 
activate the pool;
+* the `quorum-timeout` element defines the timeout to get the minimum number 
of ready targets;
+* the `local-target-enabled` element defines whether the pool has to include a 
local target;
+* the `static-connectors` element defines a list of static connectors used by 
the [static pool](#static-pool);
+* the `discovery-group` element defines the [discovery 
group](clusters.md#discovery-groups) used by the [discovery 
pool](#discovery-pool).
+
+Let's take a look at a pool example from broker.xml:
+```xml
+<pool>
+    <quorum-size>2</quorum-size>
+    <check-period>1000</check-period>
+    <local-target-enabled>true</local-target-enabled>
+    <static-connectors>
+        <connector-ref>connector1</connector-ref>
+        <connector-ref>connector2</connector-ref>
+        <connector-ref>connector3</connector-ref>
+    </static-connectors>
+</pool>
+```
+
+## Policies
+The policy define how to select a broker from a pool. The included policies 
are:
+* `FIRST_ELEMENT` to select the first target broker from the pool which is 
ready. It is useful to select the ready target brokers
+  according to the priority defined with their sequence order, ie supposing 
there are 2 target brokers
+  this policy selects the second target broker only when the first target 
broker isn't ready.
+* `ROUND_ROBIN` to select a target sequentially from a pool, this policy is 
useful to evenly distribute;
+* `CONSISTENT_HASH` to select a target by a key. This policy always selects 
the same target broker for the same key until it is removed from the pool.
+* `LEAST_CONNECTIONS` to select the targets with the fewest active 
connections. This policy helps you maintain an equal distribution of active 
connections with the target brokers.
+
+A policy is defined by the `policy` element. Let's take a look at a policy 
example from broker.xml:
+```xml
+<policy name="FIRST_ELEMENT"/>
+```
+
+## Cache
+The broker balancer provides a cache with a timeout to improve the stickiness 
of the target broker selected.
+The broker balancer returns the same target broker for a target key as long as 
it is present in the cache and is ready.
+By default, the cache is enabled, to disable the cache the `cache-timeout` 
parameter must be `0`.
+
+## Defining broker balancers
+A broker balancer is defined by `broker-balancer` element, it includes the 
following items:
+* the `name` attribute defines the name of the broker balancer;
+* the `target-key` element defines what key to select a target broker, the 
supported values are: `CLIENT_ID`, `SNI_HOST`, `SOURCE_IP`, `USER_NAME`.
+* the `target-key-filter` element defines a regular expression to filter the 
resolved keys;
+* the `local-target-filter` element defines a regular expression to filter the 
keys that have to return a local target;
+* the `cache-timeout` element is the time period for a target broker to remain 
in the cache;
+* the `pool` element defines the pool to group the target brokers, see 
[pools](#pools).
+* the `policy` element defines the policy used to select the target brokers, 
see [policies](#policies);
+
+Let's take a look at some broker balancer examples from broker.xml:
+```xml
+<broker-balancers>
+    <broker-balancer name="simple-balancer">
+        <policy name="FIRST_ELEMENT"/>
+        <pool>
+            <static-connectors>
+                <connector-ref>connector1</connector-ref>
+                <connector-ref>connector2</connector-ref>
+                <connector-ref>connector3</connector-ref>
+            </static-connectors>
+        </pool>
+    </broker-balancer>
+    <broker-balancer name="consistent-hash-balancer">
+        <target-key>USER_NAME</target-key>
+        <local-filter>admin</local-filter>
+        <policy name="CONSISTENT_HASH"/>
+        <pool>
+            <local-target-enabled>true</local-target-enabled>
+            <discovery-group-ref discovery-group-name="dg1"/>
+        </pool>
+    <policy name="CONSISTENT_HASH"/>
+    </broker-balancer>
+    <broker-balancer name="least-connections-balancer">
+        <policy name="LEAST_CONNECTIONS"/>
+        <pool>
+            <discovery-group-ref discovery-group-name="dg2"/>
+        </pool>
+    </broker-balancer>

Review comment:
       Seems odd in a way that the example of 'least connections' here is 
totally different than the one then given in the examples. I presume this one 
would behaves more like how I thought the other one might, since it has no 
target key to cache on?

##########
File path: 
examples/features/broker-balancer/evenly-redirect/src/main/java/org/apache/activemq/artemis/jms/example/EvenlyRedirectExample.java
##########
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.artemis.jms.example;
+
+import javax.jms.Connection;
+import javax.jms.ConnectionFactory;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+
+import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory;
+
+/**
+ * This example demonstrates how incoming client connections are evenly 
redirected across two brokers
+ * using a third broker with a broker balancer to redirect incoming client 
connections.
+ */
+public class EvenlyRedirectExample {
+
+   public static void main(final String[] args) throws Exception {
+
+      /**
+       * Step 1. Create a connection for producer0 and producer1, and send a 
few messages.
+       * the server0 will redirect the connection of each producer to a 
different target brokers.
+       */
+      ConnectionFactory connectionFactoryProducer0 = new 
ActiveMQConnectionFactory("tcp://localhost:61616?ha=true&reconnectAttempts=30&clientID=FOO_PRODUCER");
+      ConnectionFactory connectionFactoryProducer1 = new 
ActiveMQConnectionFactory("tcp://localhost:61616?ha=true&reconnectAttempts=30&clientID=TOO_PRODUCER");
+
+      Connection connectionProducer0 = null;
+      Connection connectionProducer1 = null;
+
+      try {
+         connectionProducer0 = connectionFactoryProducer0.createConnection();
+         connectionProducer1 = connectionFactoryProducer1.createConnection();
+
+         for (Connection connectionProducer : new Connection[] 
{connectionProducer0, connectionProducer1}) {
+            Session session = connectionProducer.createSession(false, 
Session.AUTO_ACKNOWLEDGE);
+
+            Queue queue = session.createQueue("exampleQueue");
+            MessageProducer sender = session.createProducer(queue);
+            for (int i = 0; i < 100; i++) {
+               sender.send(session.createTextMessage("Hello world n" + i + " - 
" + connectionProducer.getClientID()));
+            }
+         }
+      } finally {
+         if (connectionProducer0 != null) {
+            connectionProducer0.close();
+         }
+
+         if (connectionProducer1 != null) {
+            connectionProducer1.close();
+         }
+      }
+
+      /**
+       * Step 2. create a connection for consumer0 and consumer1, and receive 
a few messages.
+       * the server0 will redirect the connection to the same target broker of 
the respective producer
+       * because the consumer and the producer share the same clientID prefix.
+       */
+      ConnectionFactory connectionFactoryConsumer0 = new 
ActiveMQConnectionFactory("tcp://localhost:61616?ha=true&reconnectAttempts=30&clientID=TOO_CONSUMER");
+      ConnectionFactory connectionFactoryConsumer1 = new 
ActiveMQConnectionFactory("tcp://localhost:61616?ha=true&reconnectAttempts=30&clientID=FOO_CONSUMER");

Review comment:
       ```suggestion
         ConnectionFactory connectionFactoryConsumer0 = new 
ActiveMQConnectionFactory("tcp://localhost:61616?ha=true&reconnectAttempts=30&clientID=BAR_CONSUMER");
         ConnectionFactory connectionFactoryConsumer1 = new 
ActiveMQConnectionFactory("tcp://localhost:61616?ha=true&reconnectAttempts=30&clientID=FOO_CONSUMER");
   ```

##########
File path: 
examples/features/broker-balancer/symmetric-redirect/src/main/java/org/apache/activemq/artemis/jms/example/SymmetricRedirectExample.java
##########
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.artemis.jms.example;
+
+import javax.jms.Connection;
+import javax.jms.ConnectionFactory;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+
+import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory;
+
+/**
+ * This example demonstrates how incoming client connections are distributed 
across two brokers
+ * using a symmetric architecture.
+ */
+public class SymmetricRedirectExample {
+
+   public static void main(final String[] args) throws Exception {
+
+      /**
+       * Step 1. Create a connection for producer0 and producer1, and send a 
few messages.
+       * the server0 will redirect the connection of each producer to a 
different target brokers.
+       */
+      ConnectionFactory connectionFactory0Server0 = new 
ActiveMQConnectionFactory("tcp://localhost:61616?ha=true&reconnectAttempts=30&clientID=FOO_PRODUCER");
+      ConnectionFactory connectionFactory1Server0 = new 
ActiveMQConnectionFactory("tcp://localhost:61616?ha=true&reconnectAttempts=30&clientID=TOO_PRODUCER");
+
+      Connection connectionProducer0 = null;
+      Connection connectionProducer1 = null;
+
+      try {
+         connectionProducer0 = connectionFactory0Server0.createConnection();
+         connectionProducer1 = connectionFactory1Server0.createConnection();
+
+         for (Connection connectionProducer : new Connection[] 
{connectionProducer0, connectionProducer1}) {
+            Session session = connectionProducer.createSession(false, 
Session.AUTO_ACKNOWLEDGE);
+
+            Queue queue = session.createQueue("exampleQueue" + 
connectionProducer.getClientID().substring(0, 3));
+            MessageProducer sender = session.createProducer(queue);
+            for (int i = 0; i < 100; i++) {
+               TextMessage message = session.createTextMessage("Hello world n" 
+ i + " - " + connectionProducer.getClientID().substring(0, 3));
+               System.out.println("Sending message " + message.getText() + "/" 
+ connectionProducer.getClientID());
+               sender.send(message);
+            }
+         }
+      } finally {
+         if (connectionProducer0 != null) {
+            connectionProducer0.close();
+         }
+
+         if (connectionProducer1 != null) {
+            connectionProducer1.close();
+         }
+      }
+
+      /**
+       * Step 2. create a connection for consumer0 and consumer1, and receive 
a few messages.
+       * the server1 will redirect the connection to the same target broker of 
the respective producer
+       * from earlier as the new consumer connection uses the same ClientID 
prefix.
+       */
+      ConnectionFactory connectionFactory0Server1 = new 
ActiveMQConnectionFactory("tcp://localhost:61617?ha=true&reconnectAttempts=30&clientID=TOO_CONSUMER");
+      ConnectionFactory connectionFactory1Server1 = new 
ActiveMQConnectionFactory("tcp://localhost:61617?ha=true&reconnectAttempts=30&clientID=FOO_CONSUMER");

Review comment:
       ```suggestion
         ConnectionFactory connectionFactory0Server1 = new 
ActiveMQConnectionFactory("tcp://localhost:61617?ha=true&reconnectAttempts=30&clientID=BAR_CONSUMER");
         ConnectionFactory connectionFactory1Server1 = new 
ActiveMQConnectionFactory("tcp://localhost:61617?ha=true&reconnectAttempts=30&clientID=FOO_CONSUMER");
   ```

##########
File path: 
examples/features/broker-balancer/symmetric-redirect/src/main/java/org/apache/activemq/artemis/jms/example/SymmetricRedirectExample.java
##########
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.artemis.jms.example;
+
+import javax.jms.Connection;
+import javax.jms.ConnectionFactory;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageProducer;
+import javax.jms.Queue;
+import javax.jms.Session;
+import javax.jms.TextMessage;
+
+import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory;
+
+/**
+ * This example demonstrates how incoming client connections are distributed 
across two brokers
+ * using a symmetric architecture.
+ */
+public class SymmetricRedirectExample {
+
+   public static void main(final String[] args) throws Exception {
+
+      /**
+       * Step 1. Create a connection for producer0 and producer1, and send a 
few messages.
+       * the server0 will redirect the connection of each producer to a 
different target brokers.
+       */
+      ConnectionFactory connectionFactory0Server0 = new 
ActiveMQConnectionFactory("tcp://localhost:61616?ha=true&reconnectAttempts=30&clientID=FOO_PRODUCER");
+      ConnectionFactory connectionFactory1Server0 = new 
ActiveMQConnectionFactory("tcp://localhost:61616?ha=true&reconnectAttempts=30&clientID=TOO_PRODUCER");

Review comment:
       ```suggestion
          * the server0 will redirect the connection of each producer to a 
different target broker.
          */
         ConnectionFactory connectionFactory0Server0 = new 
ActiveMQConnectionFactory("tcp://localhost:61616?ha=true&reconnectAttempts=30&clientID=FOO_PRODUCER");
         ConnectionFactory connectionFactory1Server0 = new 
ActiveMQConnectionFactory("tcp://localhost:61616?ha=true&reconnectAttempts=30&clientID=BAR_PRODUCER");
   ```




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


Issue Time Tracking
-------------------

    Worklog Id:     (was: 634216)
    Time Spent: 7.5h  (was: 7h 20m)

> Broker Balancers
> ----------------
>
>                 Key: ARTEMIS-3365
>                 URL: https://issues.apache.org/jira/browse/ARTEMIS-3365
>             Project: ActiveMQ Artemis
>          Issue Type: New Feature
>            Reporter: Domenico Francesco Bruscino
>            Assignee: Domenico Francesco Bruscino
>            Priority: Major
>          Time Spent: 7.5h
>  Remaining Estimate: 0h
>
> This feature adds the broker balancers to distribute the incoming client 
> connections across multiple brokers.
> It provides a native redirection for supported clients and a new management 
> API for other clients. The native redirection can be enabled per acceptor and 
> is supported only for CORE and AMQP clients.
> See the [draft 
> documentation|https://github.com/brusdev/activemq-artemis/blob/broker_balancers/docs/user-manual/en/broker-balancers.md]
>  for further details. 



--
This message was sent by Atlassian Jira
(v8.3.4#803005)

Reply via email to