Implementing AMQ-4788 - Adding tests for the ZooKeeper variant of the partition broker plugin.

This commit is contained in:
Hiram Chirino 2013-10-08 11:09:34 -04:00
parent ef64b057a6
commit 25f70ad483
5 changed files with 182 additions and 19 deletions

View File

@ -130,6 +130,7 @@ public class PartitionBroker extends BrokerFilter {
String connectionString = getConnectionString(targetDTO.ids); String connectionString = getConnectionString(targetDTO.ids);
if( connectionString==null ) { if( connectionString==null ) {
LOG.debug("Could not convert to partition targets to connection string: " + targetDTO.ids); LOG.debug("Could not convert to partition targets to connection string: " + targetDTO.ids);
return;
} }
LOG.info("Redirecting connection to: " + connectionString); LOG.info("Redirecting connection to: " + connectionString);
@ -141,11 +142,9 @@ public class PartitionBroker extends BrokerFilter {
} }
protected String getConnectionString(HashSet<String> ids) { protected String getConnectionString(HashSet<String> ids) {
if( getConfig().brokers==null || getConfig().brokers.isEmpty() )
return null;
StringBuilder rc = new StringBuilder(); StringBuilder rc = new StringBuilder();
for (String id : ids) { for (String id : ids) {
String url = getConfig().brokers.get(id); String url = plugin.getBrokerURL(this, id);
if( url!=null ) { if( url!=null ) {
if( rc.length()!=0 ) { if( rc.length()!=0 ) {
rc.append(','); rc.append(',');
@ -153,6 +152,8 @@ public class PartitionBroker extends BrokerFilter {
rc.append(url); rc.append(url);
} }
} }
if( rc.length()==0 )
return null;
return rc.toString(); return rc.toString();
} }
@ -270,16 +271,22 @@ public class PartitionBroker extends BrokerFilter {
@Override @Override
public void addConnection(ConnectionContext context, ConnectionInfo info) throws Exception { public void addConnection(ConnectionContext context, ConnectionInfo info) throws Exception {
ConnectionMonitor monitor = new ConnectionMonitor(context); if( info.isFaultTolerant() ) {
monitors.put(info.getConnectionId(), monitor); ConnectionMonitor monitor = new ConnectionMonitor(context);
super.addConnection(context, info); monitors.put(info.getConnectionId(), monitor);
checkTarget(monitor); super.addConnection(context, info);
checkTarget(monitor);
} else {
super.addConnection(context, info);
}
} }
@Override @Override
public void removeConnection(ConnectionContext context, ConnectionInfo info, Throwable error) throws Exception { public void removeConnection(ConnectionContext context, ConnectionInfo info, Throwable error) throws Exception {
super.removeConnection(context, info, error); super.removeConnection(context, info, error);
ConnectionMonitor removed = monitors.remove(info.getConnectionId()); if( info.isFaultTolerant() ) {
monitors.remove(info.getConnectionId());
}
} }
@Override @Override

View File

@ -58,4 +58,10 @@ public class PartitionBrokerPlugin implements BrokerPlugin {
this.config = Partitioning.MAPPER.readValue(config, Partitioning.class); this.config = Partitioning.MAPPER.readValue(config, Partitioning.class);
} }
public String getBrokerURL(PartitionBroker partitionBroker, String id) {
if( config!=null && config.brokers!=null ) {
return config.brokers.get(id);
}
return null;
}
} }

View File

@ -26,6 +26,9 @@ import org.linkedin.util.clock.Timespan;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/** /**
*/ */
public class ZooKeeperPartitionBroker extends PartitionBroker { public class ZooKeeperPartitionBroker extends PartitionBroker {
@ -34,11 +37,20 @@ public class ZooKeeperPartitionBroker extends PartitionBroker {
protected volatile ZKClient zk_client = null; protected volatile ZKClient zk_client = null;
protected volatile Partitioning config; protected volatile Partitioning config;
protected final CountDownLatch configAcquired = new CountDownLatch(1);
public ZooKeeperPartitionBroker(Broker broker, ZooKeeperPartitionBrokerPlugin plugin) { public ZooKeeperPartitionBroker(Broker broker, ZooKeeperPartitionBrokerPlugin plugin) {
super(broker, plugin); super(broker, plugin);
} }
@Override
public void start() throws Exception {
super.start();
// Lets block a bit until we get our config.. Otherwise just keep
// on going.. not a big deal if we get our config later. Perhaps
// ZK service is not having a good day.
configAcquired.await(5, TimeUnit.SECONDS);
}
@Override @Override
protected void onMonitorStop() { protected void onMonitorStop() {
@ -96,6 +108,7 @@ public class ZooKeeperPartitionBroker extends PartitionBroker {
monitorWakeup(); monitorWakeup();
} }
}, stat); }, stat);
configAcquired.countDown();
reloadConfigOnPoll = false; reloadConfigOnPoll = false;
} catch (Exception e) { } catch (Exception e) {
LOG.warn("Could load partitioning configuration: " + e, e); LOG.warn("Could load partitioning configuration: " + e, e);

View File

@ -23,6 +23,9 @@ import org.apache.activemq.broker.BrokerService;
import org.apache.activemq.broker.TransportConnector; import org.apache.activemq.broker.TransportConnector;
import org.apache.activemq.partition.dto.Partitioning; import org.apache.activemq.partition.dto.Partitioning;
import org.apache.activemq.partition.dto.Target; import org.apache.activemq.partition.dto.Target;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import javax.jms.*; import javax.jms.*;
import java.io.IOException; import java.io.IOException;
@ -31,23 +34,51 @@ import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static org.junit.Assert.*;
/** /**
* Unit tests for the PartitionBroker plugin. * Unit tests for the PartitionBroker plugin.
*/ */
public class PartitionBrokerTest extends AutoFailTestSupport { public class PartitionBrokerTest {
protected HashMap<String, BrokerService> brokers = new HashMap<String, BrokerService>(); protected HashMap<String, BrokerService> brokers = new HashMap<String, BrokerService>();
protected ArrayList<Connection> connections = new ArrayList<Connection>(); protected ArrayList<Connection> connections = new ArrayList<Connection>();
Partitioning partitioning; Partitioning partitioning;
@Override @Before
protected void setUp() throws Exception { public void setUp() throws Exception {
super.setUp();
partitioning = new Partitioning(); partitioning = new Partitioning();
partitioning.brokers = new HashMap<String, String>(); partitioning.brokers = new HashMap<String, String>();
} }
/**
* Partitioning can only re-direct failover clients since those
* can re-connect and re-establish their state with another broker.
*/
@Test(timeout = 1000*60*60)
public void testNonFailoverClientHasNoPartitionEffect() throws Exception {
partitioning.byClientId = new HashMap<String, Target>();
partitioning.byClientId.put("client1", new Target("broker1"));
createBrokerCluster(2);
Connection connection = createConnectionToUrl(getConnectURL("broker2"));
within(5, TimeUnit.SECONDS, new Task() {
public void run() throws Exception {
assertEquals(0, getTransportConnector("broker1").getConnections().size());
assertEquals(1, getTransportConnector("broker2").getConnections().size());
}
});
connection.setClientID("client1");
connection.start();
Thread.sleep(1000);
assertEquals(0, getTransportConnector("broker1").getConnections().size());
assertEquals(1, getTransportConnector("broker2").getConnections().size());
}
@Test(timeout = 1000*60*60)
public void testPartitionByClientId() throws Exception { public void testPartitionByClientId() throws Exception {
partitioning.byClientId = new HashMap<String, Target>(); partitioning.byClientId = new HashMap<String, Target>();
partitioning.byClientId.put("client1", new Target("broker1")); partitioning.byClientId.put("client1", new Target("broker1"));
@ -73,6 +104,7 @@ public class PartitionBrokerTest extends AutoFailTestSupport {
}); });
} }
@Test(timeout = 1000*60*60)
public void testPartitionByQueue() throws Exception { public void testPartitionByQueue() throws Exception {
partitioning.byQueue = new HashMap<String, Target>(); partitioning.byQueue = new HashMap<String, Target>();
partitioning.byQueue.put("foo", new Target("broker1")); partitioning.byQueue.put("foo", new Target("broker1"));
@ -149,7 +181,10 @@ public class PartitionBrokerTest extends AutoFailTestSupport {
} }
protected Connection createConnectionTo(String brokerId) throws IOException, URISyntaxException, JMSException { protected Connection createConnectionTo(String brokerId) throws IOException, URISyntaxException, JMSException {
String url = "failover://(" + getConnectURL(brokerId) + ")"; return createConnectionToUrl("failover://(" + getConnectURL(brokerId) + ")");
}
private Connection createConnectionToUrl(String url) throws JMSException {
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(url); ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(url);
Connection connection = factory.createConnection(); Connection connection = factory.createConnection();
connections.add(connection); connections.add(connection);
@ -174,16 +209,20 @@ public class PartitionBrokerTest extends AutoFailTestSupport {
String brokerId = "broker" + i; String brokerId = "broker" + i;
BrokerService broker = createBroker(brokerId); BrokerService broker = createBroker(brokerId);
broker.setPersistent(false); broker.setPersistent(false);
PartitionBrokerPlugin plugin = new PartitionBrokerPlugin();
plugin.setConfig(partitioning);
broker.setPlugins(new BrokerPlugin[]{plugin});
broker.addConnector("tcp://localhost:0").setName("tcp"); broker.addConnector("tcp://localhost:0").setName("tcp");
addPartitionBrokerPlugin(broker);
broker.start(); broker.start();
broker.waitUntilStarted(); broker.waitUntilStarted();
partitioning.brokers.put(brokerId, getConnectURL(brokerId)); partitioning.brokers.put(brokerId, getConnectURL(brokerId));
} }
} }
protected void addPartitionBrokerPlugin(BrokerService broker) {
PartitionBrokerPlugin plugin = new PartitionBrokerPlugin();
plugin.setConfig(partitioning);
broker.setPlugins(new BrokerPlugin[]{plugin});
}
protected BrokerService createBroker(String name) { protected BrokerService createBroker(String name) {
BrokerService broker = new BrokerService(); BrokerService broker = new BrokerService();
broker.setBrokerName(name); broker.setBrokerName(name);
@ -191,8 +230,8 @@ public class PartitionBrokerTest extends AutoFailTestSupport {
return broker; return broker;
} }
@Override @After
protected void tearDown() throws Exception { public void tearDown() throws Exception {
for (Connection connection : connections) { for (Connection connection : connections) {
try { try {
connection.close(); connection.close();
@ -208,7 +247,6 @@ public class PartitionBrokerTest extends AutoFailTestSupport {
} }
} }
brokers.clear(); brokers.clear();
super.tearDown();
} }
} }

View File

@ -0,0 +1,99 @@
/**
* 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.partition;
import org.apache.activemq.broker.BrokerPlugin;
import org.apache.activemq.broker.BrokerService;
import org.apache.activemq.leveldb.replicated.groups.ZKClient;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.server.NIOServerCnxnFactory;
import org.apache.zookeeper.server.ZooKeeperServer;
import org.apache.zookeeper.server.persistence.FileTxnSnapLog;
import org.junit.After;
import org.junit.Before;
import org.linkedin.util.clock.Timespan;
import java.io.File;
import java.net.InetSocketAddress;
/**
*/
public class ZooKeeperPartitionBrokerTest extends PartitionBrokerTest {
NIOServerCnxnFactory connector;
@Before
public void setUp() throws Exception {
System.out.println("Starting ZooKeeper");
ZooKeeperServer zk_server = new ZooKeeperServer();
zk_server.setTickTime(500);
zk_server.setTxnLogFactory(new FileTxnSnapLog(new File("target/test-data/zk-log"), new File("target/test-data/zk-data")));
connector = new NIOServerCnxnFactory();
connector.configure(new InetSocketAddress(0), 100);
connector.startup(zk_server);
System.out.println("ZooKeeper Started");
super.setUp();
}
@After
public void tearDown() throws Exception {
super.tearDown();
if( connector!=null ) {
connector.shutdown();
connector = null;
}
}
String zkPath = "/partition-config";
@Override
protected void createBrokerCluster(int brokerCount) throws Exception {
// Store the partitioning in ZK.
ZKClient zk_client = new ZKClient("localhost:" + connector.getLocalPort(), Timespan.parse("10s"), null);
try {
zk_client.start();
zk_client.waitForConnected(Timespan.parse("30s"));
try {
zk_client.delete(zkPath);
} catch (Throwable e) {
}
zk_client.create(zkPath, partitioning.toString(), CreateMode.PERSISTENT);
} finally {
zk_client.close();
}
super.createBrokerCluster(brokerCount);
}
@Override
protected void addPartitionBrokerPlugin(BrokerService broker) {
// Have the borker plugin get the partition config via ZK.
ZooKeeperPartitionBrokerPlugin plugin = new ZooKeeperPartitionBrokerPlugin(){
@Override
public String getBrokerURL(PartitionBroker partitionBroker, String id) {
try {
return getConnectURL(id);
} catch (Exception e) {
return null;
}
}
};
plugin.setZkAddress("localhost:" + connector.getLocalPort());
plugin.setZkPath(zkPath);
broker.setPlugins(new BrokerPlugin[]{plugin});
}
}