package com.leansoft.luxun.load; import static org.junit.Assert.*; import java.io.File; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Queue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.atomic.AtomicInteger; import org.junit.After; import org.junit.Before; import org.junit.Test; import com.leansoft.luxun.common.exception.ConsumerTimeoutException; import com.leansoft.luxun.consumer.ConsumerConfig; import com.leansoft.luxun.consumer.IStreamFactory; import com.leansoft.luxun.consumer.MessageStream; import com.leansoft.luxun.consumer.StreamFactory; import com.leansoft.luxun.producer.IProducer; import com.leansoft.luxun.producer.ProducerConfig; import com.leansoft.luxun.producer.ProducerData; import com.leansoft.luxun.serializer.StringDecoder; import com.leansoft.luxun.serializer.StringEncoder; import com.leansoft.luxun.server.LuxunServer; import com.leansoft.luxun.server.ServerConfig; import com.leansoft.luxun.utils.ImmutableMap; import com.leansoft.luxun.utils.TestUtils; import com.leansoft.luxun.utils.Utils; public class StreamLoadTest { private int port1 = 9092; private int brokerId1 = 0; private int port2 = 9093; private int brokerId2 = 1; private LuxunServer server1 = null; private LuxunServer server2 = null; private String brokerList = brokerId1 + ":127.0.0.1:" + port1 + "," + brokerId2 + ":127.0.0.1:" + port2; @Before public void setup() { Properties props1 = TestUtils.createBrokerConfig(brokerId1, port1); ServerConfig config1 = new ServerConfig(props1); server1 = TestUtils.createServer(config1); Properties props2 = TestUtils.createBrokerConfig(brokerId2, port2); ServerConfig config2 = new ServerConfig(props2); server2 = TestUtils.createServer(config2); } @After public void clean() throws Exception { server1.close(); server2.close(); Utils.deleteDirectory(new File(server1.config.getLogDir())); Utils.deleteDirectory(new File(server2.config.getLogDir())); Thread.sleep(500); } // configurable parameters ////////////////////////////////////////////////////////////////// private static int loop = 3; private static int topicFishTotalItemCount = 100000; private static int topicBirdTotalItemCount = 200000; private static int topicFishProducerNum = 2; private static int topicBirdProducerNum = 4; private static int topicFishConsumerGroupANum = 2; private static int topicFishConsumerGroupBNum = 4; private static int topicBirdConsumerGroupANum = 1; private static int topicBirdConsumerGroupBNum = 2; private static int messageLength = 1024; ////////////////////////////////////////////////////////////////// private static enum Status { ERROR, SUCCESS } private static class Result { Status status; } private static final String TOPIC_FISH = "fish"; private static final String TOPIC_BIRD = "bird"; private static final AtomicInteger topicFishProducingItemCount = new AtomicInteger(0); private static final AtomicInteger topicBirdProducingItemCount = new AtomicInteger(0); private static final Map<String, AtomicInteger> topicFishItemMap = new ConcurrentHashMap<String,AtomicInteger>(); private static final Map<String, AtomicInteger> topicBirdItemMap = new ConcurrentHashMap<String,AtomicInteger>(); private static class TopicFishProducerThread extends Thread { private final CountDownLatch latch; private final Queue<Result> resultQueue; private final IProducer<String, String> stringProducer; public TopicFishProducerThread(CountDownLatch latch, Queue<Result> resultQueue, IProducer<String, String> stringProducer) { this.latch = latch; this.resultQueue = resultQueue; this.stringProducer = stringProducer; } public void run() { Result result = new Result(); String rndString = TestUtils.randomString(messageLength); try { latch.countDown(); latch.await(); while(true) { int count = topicFishProducingItemCount.incrementAndGet(); if(count > topicFishTotalItemCount) break; String item = rndString + '-' + count; topicFishItemMap.put(item, new AtomicInteger(0)); stringProducer.send(new ProducerData<String, String>(TOPIC_FISH, item)); } result.status = Status.SUCCESS; } catch (Exception e) { e.printStackTrace(); result.status = Status.ERROR; } resultQueue.offer(result); } } private static class TopicBirdProducerThread extends Thread { private final CountDownLatch latch; private final Queue<Result> resultQueue; private final IProducer<String, String> stringProducer; public TopicBirdProducerThread(CountDownLatch latch, Queue<Result> resultQueue, IProducer<String, String> stringProducer) { this.latch = latch; this.resultQueue = resultQueue; this.stringProducer = stringProducer; } public void run() { Result result = new Result(); String rndString = TestUtils.randomString(messageLength); try { latch.countDown(); latch.await(); while(true) { int count = topicBirdProducingItemCount.incrementAndGet(); if(count > topicBirdTotalItemCount) break; String item = rndString + '-' + count; topicBirdItemMap.put(item, new AtomicInteger(0)); stringProducer.send(new ProducerData<String, String>(TOPIC_BIRD, item)); } result.status = Status.SUCCESS; } catch (Exception e) { e.printStackTrace(); result.status = Status.ERROR; } resultQueue.offer(result); } } private static class TopicFishConsumerThread extends Thread { private final CountDownLatch latch; private final Queue<Result> resultQueue; private final MessageStream<String> stream; private final AtomicInteger itemCount; public TopicFishConsumerThread(CountDownLatch latch, Queue<Result> resultQueue, MessageStream<String> stream, AtomicInteger itemCount) { this.latch = latch; this.resultQueue = resultQueue; this.stream = stream; this.itemCount = itemCount; } public void run() { Result result = new Result(); try { latch.countDown(); latch.await(); try { while (itemCount.get() < topicFishTotalItemCount) { String item = stream.iterator().next(); AtomicInteger counter = topicFishItemMap.get(item); assertNotNull(counter); counter.incrementAndGet(); itemCount.incrementAndGet(); } } catch (ConsumerTimeoutException cte) { // expected } result.status = Status.SUCCESS; } catch (Exception e) { e.printStackTrace(); result.status = Status.ERROR; } resultQueue.offer(result); } } private static class TopicBirdConsumerThread extends Thread { private final CountDownLatch latch; private final Queue<Result> resultQueue; private final MessageStream<String> stream; private final AtomicInteger itemCount; public TopicBirdConsumerThread(CountDownLatch latch, Queue<Result> resultQueue, MessageStream<String> stream, AtomicInteger itemCount) { this.latch = latch; this.resultQueue = resultQueue; this.stream = stream; this.itemCount = itemCount; } public void run() { Result result = new Result(); try { latch.countDown(); latch.await(); try { while (itemCount.get() < topicBirdTotalItemCount) { String item = stream.iterator().next(); AtomicInteger counter = topicBirdItemMap.get(item); assertNotNull(counter); counter.incrementAndGet(); itemCount.incrementAndGet(); } } catch (ConsumerTimeoutException cte) { // expected } result.status = Status.SUCCESS; } catch (Exception e) { e.printStackTrace(); result.status = Status.ERROR; } resultQueue.offer(result); } } @SuppressWarnings("unchecked") public void doRunMixed(boolean async) throws Exception { //prepare CountDownLatch allLatch = new CountDownLatch(topicFishProducerNum + topicBirdProducerNum + topicFishConsumerGroupANum + topicBirdConsumerGroupANum + topicFishConsumerGroupBNum + topicBirdConsumerGroupBNum); IProducer<String, String>[] topicFishProducers = new IProducer[topicFishProducerNum]; IProducer<String, String>[] topicBirdProducers = new IProducer[topicBirdProducerNum]; BlockingQueue<Result> producerResults = new LinkedBlockingQueue<Result>(); BlockingQueue<Result> consumerResults = new LinkedBlockingQueue<Result>(); //run testing for(int i = 0; i < topicFishProducerNum; i++) { Properties props = new Properties(); props.put("serializer.class", StringEncoder.class.getName()); props.put("broker.list", this.brokerList); if (async) { props.put("producer.type", "async"); props.put("queue.enqueueTimeout.ms", "-1"); props.put("queue.time", "500"); } ProducerConfig config = new ProducerConfig(props); IProducer<String, String> stringProducer = new com.leansoft.luxun.producer.Producer<String, String>(config); topicFishProducers[i] = stringProducer; TopicFishProducerThread p = new TopicFishProducerThread(allLatch, producerResults, stringProducer); p.start(); } for(int i = 0; i < topicBirdProducerNum; i++) { Properties props = new Properties(); props.put("serializer.class", StringEncoder.class.getName()); props.put("broker.list", this.brokerList); if (async) { props.put("producer.type", "async"); props.put("queue.enqueueTimeout.ms", "-1"); props.put("queue.time", "500"); } ProducerConfig config = new ProducerConfig(props); IProducer<String, String> stringProducer = new com.leansoft.luxun.producer.Producer<String, String>(config); topicBirdProducers[i] = stringProducer; TopicBirdProducerThread p = new TopicBirdProducerThread(allLatch, producerResults, stringProducer); p.start(); } AtomicInteger topicFishConsumerGroupAItemCount = new AtomicInteger(0); AtomicInteger topicBirdConsumerGroupAItemCount = new AtomicInteger(0); Properties props = TestUtils.createConsumerProperties(brokerList, "group-a", "consumer-a", 5000); ConsumerConfig groupAConsumerConfig = new ConsumerConfig(props); IStreamFactory groupAStreamFactory = new StreamFactory(groupAConsumerConfig); Map<String, List<MessageStream<String>>> groupAStreams = groupAStreamFactory.createMessageStreams( ImmutableMap.of(TOPIC_FISH, topicFishConsumerGroupANum, TOPIC_BIRD, topicBirdConsumerGroupANum), new StringDecoder()); List<MessageStream<String>> topicFishGroupAStreams = groupAStreams.get(TOPIC_FISH); for(MessageStream<String> stream : topicFishGroupAStreams) { TopicFishConsumerThread c = new TopicFishConsumerThread(allLatch, consumerResults, stream, topicFishConsumerGroupAItemCount); c.start(); } List<MessageStream<String>> topicBirdGroupAStreams = groupAStreams.get(TOPIC_BIRD); for(MessageStream<String> stream : topicBirdGroupAStreams) { TopicBirdConsumerThread c = new TopicBirdConsumerThread(allLatch, consumerResults, stream, topicBirdConsumerGroupAItemCount); c.start(); } AtomicInteger topicFishConsumerGroupBItemCount = new AtomicInteger(0); AtomicInteger topicBirdConsumerGroupBItemCount = new AtomicInteger(0); props = TestUtils.createConsumerProperties(brokerList, "group-b", "consumer-b", 5000); ConsumerConfig groupBConsumerConfig = new ConsumerConfig(props); IStreamFactory groupBStreamFactory = new StreamFactory(groupBConsumerConfig); Map<String, List<MessageStream<String>>> groupBStreams = groupBStreamFactory.createMessageStreams( ImmutableMap.of(TOPIC_FISH, topicFishConsumerGroupBNum, TOPIC_BIRD, topicBirdConsumerGroupBNum), new StringDecoder()); List<MessageStream<String>> topicFishGroupBStreams = groupBStreams.get(TOPIC_FISH); for(MessageStream<String> stream : topicFishGroupBStreams) { TopicFishConsumerThread c = new TopicFishConsumerThread(allLatch, consumerResults, stream, topicFishConsumerGroupBItemCount); c.start(); } List<MessageStream<String>> topicBirdGroupBStreams = groupBStreams.get(TOPIC_BIRD); for(MessageStream<String> stream : topicBirdGroupBStreams) { TopicBirdConsumerThread c = new TopicBirdConsumerThread(allLatch, consumerResults, stream, topicBirdConsumerGroupBItemCount); c.start(); } //verify for(int i = 0; i < topicFishProducerNum + topicBirdProducerNum; i++) { Result result = producerResults.take(); assertEquals(result.status, Status.SUCCESS); } for(int i = 0; i < topicFishConsumerGroupANum + topicBirdConsumerGroupANum + topicFishConsumerGroupBNum + topicBirdConsumerGroupBNum; i++) { Result result = consumerResults.take(); assertEquals(result.status, Status.SUCCESS); } assertTrue(topicFishItemMap.size() == topicFishTotalItemCount); for(AtomicInteger counter : topicFishItemMap.values()) { assertTrue(counter.get() == 2); } assertTrue(topicBirdItemMap.size() == topicBirdTotalItemCount); for(AtomicInteger counter : topicBirdItemMap.values()) { assertTrue(counter.get() == 2); } // closing for(int i = 0; i < topicFishProducerNum; i++) { topicFishProducers[i].close(); } for(int i = 0; i < topicBirdProducerNum; i++) { topicBirdProducers[i].close(); } groupAStreamFactory.close(); groupBStreamFactory.close(); } @Test public void runTest() throws Exception { System.out.println("Load test begin ..."); for(int i = 0; i < loop; i++) { System.out.println("[doRunMixed sync producer] round " + (i + 1) + " of " + loop); this.doRunMixed(false); // reset topicFishProducingItemCount.set(0); topicBirdProducingItemCount.set(0); topicFishItemMap.clear(); topicBirdItemMap.clear(); } for(int i = 0; i < loop; i++) { System.out.println("[doRunMixed async producer] round " + (i + 1) + " of " + loop); this.doRunMixed(true); // reset topicFishProducingItemCount.set(0); topicBirdProducingItemCount.set(0); topicFishItemMap.clear(); topicBirdItemMap.clear(); } System.out.println("Load test finished successfully."); } }