/**
* Copyright 2012 Comcast Corporation
*
* Licensed 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 com.comcast.cqs.test.stress;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.CharArrayWriter;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.log4j.Logger;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import com.amazonaws.AmazonServiceException;
import com.comcast.cmb.common.controller.CMBControllerServlet;
import com.comcast.cmb.common.model.User;
import com.comcast.cmb.common.persistence.AbstractDurablePersistence;
import com.comcast.cmb.common.persistence.DurablePersistenceFactory;
import com.comcast.cmb.common.persistence.IUserPersistence;
import com.comcast.cmb.common.persistence.PersistenceFactory;
import com.comcast.cmb.common.persistence.UserCassandraPersistence;
import com.comcast.cmb.common.util.CMBException;
import com.comcast.cmb.common.util.CMBProperties;
import com.comcast.cmb.common.util.PersistenceException;
import com.comcast.cmb.common.util.Util;
import com.comcast.cns.io.CommunicationUtils;
import com.comcast.cqs.model.CQSMessage;
import com.comcast.cqs.persistence.ICQSMessagePersistence;
import com.comcast.cqs.util.CQSErrorCodes;
//
// some useful greps:
//
// grep -i responsetime /tmp/receiveMessage.log | grep ReceiveMessage | cut -f9- -d= > /tmp/redisTime.out
//
// grep -i responsetime /tmp/receiveMessage.log | grep ReceiveMessage | cut -f5-5 -d= > /tmp/responseTime.log
//
// grep -i responsetime /tmp/receiveMessage.log | grep ReceiveMessage | cut -f6-6 -d= > /tmp/cassandraTime.log
//
// some useful splunks:
//
// response time for receive message operation:
//
// sourcetype=cqs action=ReceiveMessage | chart perc95(responseTimeMS) perc90(responseTimeMS) avg(responseTimeMS) median(responseTimeMS) max(responseTimeMS) perc95(redisTime) perc90(redisTime) avg(redisTime) median(redisTime) max(redisTime) perc95(CassandraTimeMS) perc90(CassandraTimeMS) avg(CassandraTimeMS) median(CassandraTimeMS) max(CassandraTimeMS)
//
// sourcetype=cqs action=ReceiveMessage | timechart avg(redisTime) avg(CassandraTimeMS) avg(responseTimeMS)
//
// manage changes in message visibility, performed once a second:
//
// sourcetype=cqs event=chechAndProcessRevisibleSet | chart avg(responseTimeMS) p95(responseTimeMS)
//
// manage hidden messages every 10 sec, look for hiddenSetSize parameter:
//
// sourcetype=cqs event=RevisibleProcessor | chart avg(responseTimeMS) p95(responseTimeMS)
//
// todo: check for message order
public class CqsStressTester {
private static Logger logger = Logger.getLogger(CqsStressTester.class);
static ConcurrentHashMap<Integer, AtomicInteger> timeReceiveMessageCountMap = new ConcurrentHashMap<Integer, AtomicInteger>();
static ConcurrentHashMap<Integer, AtomicInteger> timeSendMessageCountMap = new ConcurrentHashMap<Integer, AtomicInteger>();
static Set<String> sendMessageIdSet = Collections.newSetFromMap(new ConcurrentHashMap<String,Boolean>());
//static private ConcurrentLinkedQueue<Long> receiveLacencyMSList = new ConcurrentLinkedQueue<Long>();
public long startTime = System.currentTimeMillis();
final static SchemeRegistry schemeRegistry = new SchemeRegistry();
public final static ThreadSafeClientConnManager cm;
final static HttpClient httpClient;
static {
schemeRegistry.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
schemeRegistry.register(new Scheme("https", 443, SSLSocketFactory.getSocketFactory()));
cm = new ThreadSafeClientConnManager(schemeRegistry);
// Increase max total connection to 200
cm.setMaxTotal(CMBProperties.getInstance().getCNSPublisherHttpEndpointConnectionPoolSize());
// Increase default max connection per route to 20
cm.setDefaultMaxPerRoute(CMBProperties.getInstance().getCNSPublisherHttpEndpointConnectionsPerRouteSize());
httpClient = new DefaultHttpClient(cm);
}
private List<String> queueUrls = new ArrayList<String>();
private HashMap<String, List<Receiver>> receiverMap = new HashMap<String, List<Receiver>>();
private static final String ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static HashMap<String, String> attributeParams = new HashMap<String, String>();
private static AtomicInteger messageCount = new AtomicInteger(0);
static Random rand = new Random();
static User user = null;
final static int revisiblePercentage = CQSStressTestProperties.getInstance().getRevisiblePercentage();
private static void setup() throws Exception {
attributeParams.put("MessageRetentionPeriod", "6000"); // the number of seconds when a message is available in the system - 10 hours
attributeParams.put("VisibilityTimeout", "60"); // the number of seconds a message is hidden from the next user - 1 min
try {
IUserPersistence userPersistence = new UserCassandraPersistence();
user = userPersistence.getUserByName("cqs_stress_user");
if (user == null) {
user = userPersistence.createUser("cqs_stress_user", "cqs_stress_user");
}
} catch (Exception ex) {
logger.error("Action=setup status=exception ", ex);
}
}
// This creates the specified number of queues
private ScheduledExecutorService createQueuesAndInitializePublishersAndReceivers() throws PersistenceException, NoSuchAlgorithmException, UnsupportedEncodingException, InterruptedException {
String fixedQueueName = "testQueue";
int totalNumberOfQueues = CQSStressTestProperties.getInstance().getNumberOfQueues();
String[] queueNames = CQSStressTestProperties.getInstance().getQueueNames();
if (queueNames != null) {
totalNumberOfQueues = queueNames.length;
}
ICQSMessagePersistence messagePersistence = PersistenceFactory.getCQSMessagePersistence();
for (int i=0; i<totalNumberOfQueues; i++) {
String queueName = fixedQueueName + rand.nextInt() + "_" + i;
if (queueNames != null) {
queueName = queueNames[i];
}
String myQueueUrl = createQueue(queueName);
queueUrls.add(myQueueUrl);
if (CQSStressTestProperties.getInstance().getNumberOfSendersPerQueue() > 0) {
messagePersistence.clearQueue(myQueueUrl, 0);
}
logger.info("QueueUrl" + i + " = " + myQueueUrl);
//first tickle the empty queue population by calling a receive when nothing is in the queue
receiveMessage(myQueueUrl, 1, 100);
Thread.sleep(500);
}
for (String queueUrl : queueUrls) {
createReceivers(queueUrl);
}
return createSenders(queueUrls);
}
private ScheduledExecutorService createSenders(List<String> queueUrls) {
int senderCount = CQSStressTestProperties.getInstance().getNumberOfSendersPerQueue();
if (senderCount == 0) {
return null;
}
int numberOfMessagesPerSec = CQSStressTestProperties.getInstance().getMessagesPerQueuePerSecond();
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(queueUrls.size()*senderCount);
for (String queueUrl : queueUrls) {
for (int i=0; i<senderCount; i ++) {
scheduledExecutorService.scheduleWithFixedDelay(new MessageSender(queueUrl, i), rand.nextInt(100), 1000*senderCount/numberOfMessagesPerSec, TimeUnit.MILLISECONDS);
}
}
return scheduledExecutorService;
}
private String generateRandomMessage(int length) {
StringBuilder sb = new StringBuilder(length);
Date now = new Date();
sb.append("@").append(now.getTime()).append("*");
sb.append(now).append("%");
for (int i=sb.length(); i<length; i++) {
sb.append(ALPHABET.charAt(rand.nextInt(ALPHABET.length())));
}
return sb.toString();
}
private void createReceivers(String queueUrl) {
AbstractDurablePersistence persistence = DurablePersistenceFactory.getInstance();
long receiverCount = CQSStressTestProperties.getInstance().getNumberOfReceiversPerQueue();
List<Receiver> receiverListForQueue = new ArrayList<Receiver>();
for (int i=0; i<receiverCount; i++) {
Receiver receiver = new Receiver(queueUrl, i, persistence);
receiver.start();
receiverListForQueue.add(receiver);
}
receiverMap.put(queueUrl, receiverListForQueue);
}
protected void doChart(int totalMessagesSent, int totalMessagesReceived) throws IOException {
generateChart("Send message count: Total Messages Sent=" + totalMessagesSent, timeSendMessageCountMap, "SendMessage_Run_" + startTime, new Date(startTime), new Date());
generateChart("Receive message count: Total Messages Received=" + totalMessagesReceived, timeReceiveMessageCountMap, "ReceiveMessage_Run_" + startTime, new Date(startTime), new Date());
}
private byte[] generateChart(String title, ConcurrentHashMap<Integer, AtomicInteger> metric, String id, Date startTime, Date endTime) throws IOException {
XYSeries series = new XYSeries(title);
for (Entry<Integer, AtomicInteger> entry : metric.entrySet()) {
series.add(entry.getKey().intValue(), entry.getValue().intValue());
}
XYSeriesCollection dataset = new XYSeriesCollection(series);
JFreeChart chart = ChartFactory.createXYBarChart(
"Start: " + startTime + " End: " + endTime + " Message Count: " + messageCount,
"Test Second",
false,
"Number of Messages",
dataset,
PlotOrientation.VERTICAL,
true,
true,
false
);
File file = new File("/tmp/" + id + ".jpeg");
//File file = new File(getServletContext().getRealPath("WEB-INF" + "/" + id + ".jpeg"));
ChartUtilities.saveChartAsJPEG(file, chart, 1600, 400);
//byte b[] = Files.toByteArray(file);
//return b;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ChartUtilities.writeChartAsJPEG(bos, chart, 2400, 400);
return bos.toByteArray();
}
/**
* @param args
*/
public static void main(String[] args) {
try {
CMBControllerServlet.valueAccumulator.initializeAllCounters();
setup();
Util.initLog4j();
CqsStressTester tester = new CqsStressTester();
ScheduledExecutorService scheduledExecutorService = tester.createQueuesAndInitializePublishersAndReceivers(); //will clear queues
long totalMessagesReceived = 0;
long totalMessagesDeleted = 0;
long totalMessagesRevisibled = 0;
long totalEmptyResponses = 0;
long totalDuplicates = 0;
long totalOutOfOrderMessages = 0;
int testDurationSeconds = CQSStressTestProperties.getInstance().getTestDurationSeconds();
Thread.sleep(testDurationSeconds*1000);
if (scheduledExecutorService != null) {
scheduledExecutorService.shutdown();
}
logger.info("===Sender Shutdown Triggered==");
for (String queueUrl : tester.receiverMap.keySet()) {
for (Receiver receiver : tester.receiverMap.get(queueUrl)) {
receiver.setContinueThread(false);
}
}
for (String queueUrl : tester.receiverMap.keySet()) {
Set<String> messageIdMaster = new HashSet<String>();
List<Integer> deleteTimesMaster = new ArrayList<Integer>();
List<Long> flightTimesMaster = new ArrayList<Long>();
List<Long> receiveTimesMaster = new ArrayList<Long>();
for (Receiver receiver : tester.receiverMap.get(queueUrl)) {
receiver.join();
logger.warn("===================================================================================================================");
logger.warn("TheadId=" + receiver.getThreadId() + " receiveMessageCount=" + receiver.getTotalMessagesReceived() + " deletedMessageCount=" + receiver.getTotalMessagesDeleted() + " revisibledMessageCount=" + receiver.getTotalMessagesRevisibled());
logger.warn("===================================================================================================================");
totalMessagesReceived += receiver.getTotalMessagesReceived();
totalMessagesDeleted += receiver.getTotalMessagesDeleted();
totalMessagesRevisibled += receiver.getTotalMessagesRevisibled();
totalOutOfOrderMessages += receiver.getTotalOutOfOrderMessages();
totalDuplicates += checkAndCombine(messageIdMaster, receiver.messageIds);
//deleteTimesMaster.addAll(receiver.deleteLatencyMSList);
//flightTimesMaster.addAll(receiver.flightTimeList);
totalEmptyResponses += receiver.emptyResponseCount;
}
logger.warn("===================================================================================================================");
Iterator<String> iter = sendMessageIdSet.iterator();
while (iter.hasNext()) {
logger.error("Missed message:" + iter.next());
}
Collections.sort(deleteTimesMaster);
Collections.sort(flightTimesMaster);
//receiveTimesMaster.addAll(receiveLacencyMSList);
Collections.sort(receiveTimesMaster);
/*logger.warn("Receive message latencies");
if (receiveTimesMaster.size() > 0) {
for (int i=5; i<=100; i+=5) {
int percentileIndex = receiveTimesMaster.size()*i/100 - 1;
if (percentileIndex < 0) {
percentileIndex = 0;
}
logger.warn("" + i + "th percentile=" + receiveTimesMaster.get(percentileIndex));
}
}
logger.warn("===================================================================================================================");
logger.warn("Message flight time latencies");
if (flightTimesMaster.size() > 0) {
for (int i=5; i<=100; i+=5) {
int percentileIndex = flightTimesMaster.size()*i/100 - 1;
if (percentileIndex < 0) {
percentileIndex = 0;
}
logger.warn("" + i + "th percentile=" + flightTimesMaster.get(percentileIndex));
}
}
logger.warn("===================================================================================================================");
logger.warn("Delete message latencies");
if (deleteTimesMaster.size() > 0) {
for (int i=5; i<=100; i+=5) {
int percentileIndex = deleteTimesMaster.size()*i/100 - 1;
if (percentileIndex < 0) {
percentileIndex = 0;
}
logger.warn("" + i + "th percentile=" + deleteTimesMaster.get(percentileIndex));
}
}*/
}
logger.warn("===================================================================================================================");
logger.warn("===================================================================================================================");
logger.warn("totalMessagesSent=" + tester.messageCount.get() + " totalMessagesReceived=" + totalMessagesReceived + " totalMessagesDeleted=" + totalMessagesDeleted + " totalMessagesRevisibled=" + totalMessagesRevisibled);
logger.warn("===================================================================================================================");
logger.warn("totalEmptyResponses=" + totalEmptyResponses);
logger.warn("totalDuplicates=" + totalDuplicates);
logger.warn("totalOutOfOrderMessages=" + totalOutOfOrderMessages);
logger.warn("totalMessagesLost=" + sendMessageIdSet.size());
logger.warn("===================================================================================================================");
logger.warn("totalRunTimeMillis=" + (System.currentTimeMillis()-tester.startTime) + " status=Exit");
logger.warn("===================================================================================================================");
logger.warn("===================================================================================================================");
} catch (Exception e) {
logger.error("Thread=main status=exception message=setup_failure ", e);
} finally {
CMBControllerServlet.valueAccumulator.deleteAllCounters();
}
}
private static long checkAndCombine(Set<String> masterSet, Set<String> receivedMessageIds) {
long totalDuplicates = 0;
if (masterSet == null || receivedMessageIds == null) {
return 0;
}
for (String messageId : receivedMessageIds) {
if (masterSet.contains(messageId)) {
logger.error("Action=checkAndCombine status=error message=duplicateId:" + messageId);
totalDuplicates++;
} else {
masterSet.add(messageId);
}
}
return totalDuplicates;
}
private class MessageSender implements Runnable {
private String queueUrl;
private String threadId;
public MessageSender(String queueUrl, int index) {
setQueueUrl(queueUrl);
setThreadId(queueUrl, index);
}
public String getQueueUrl() {
return queueUrl;
}
public void setQueueUrl(String queueUrl) {
this.queueUrl = queueUrl;
}
public void setThreadId(String queueUrl, int index) {
this.queueUrl = queueUrl;
if (queueUrl == null || queueUrl.length() == 0) {
return;
}
String queueName = queueUrl.substring(queueUrl.lastIndexOf('/') + 1);
this.threadId = "Sender_" + queueName + "_" + index;
}
@Override
public void run() {
try {
CMBControllerServlet.valueAccumulator.initializeAllCounters();
} catch (Exception ex) {
logger.error("Thread=" + threadId + " Action=setup status=exception ", ex);
}
int maxSendBatchSize = CQSStressTestProperties.getInstance().getSendMessageBatchSize();
maxSendBatchSize = 1; // Till we add support for send message batch
try {
String currentTime = "" + System.currentTimeMillis();
long startNanoTime = System.nanoTime();
int totalMessagesSuccessful = 0;
for (int i = 0; i < maxSendBatchSize; i++) {
//TBD: Make this a configurable parameter
String messageBodyRandom = generateRandomMessage(2000);
long index = rand.nextLong();
String messageIndex = Thread.currentThread().getId() + "_" + index;
sendMessageIdSet.add(messageIndex);
String messageBody = "currentTime=" + currentTime + " messageIndex=" + messageIndex + " messagebody=message_" + currentTime + "_" + messageBodyRandom + "_" + index;
if (sendMessage(this.queueUrl, messageBody) != null) {
totalMessagesSuccessful++;
}
logger.debug("Thread=" + threadId + " Action=Sent MessageBody '" + messageBody + "'");
}
//addSendMessageCount((int)((System.currentTimeMillis() - startTime)/1000), totalMessagesSuccessful);
long endNanoTime = System.nanoTime();
logger.info("Thread=" + threadId + " Action=SendMessageBatch latencyNano=" + (endNanoTime-startNanoTime));
int count = messageCount.addAndGet(totalMessagesSuccessful);
if (count % 100 == 0) {
logger.info("event=publish queueUrl=" + getQueueUrl() + " totalCount=" + count);
}
} catch (AmazonServiceException ase) {
displayServiceException("" + Thread.currentThread().getId(), "SendMessageBatch", ase);
} finally {
CMBControllerServlet.valueAccumulator.deleteAllCounters();
}
}
}
class Receiver extends Thread {
private String queueUrl;
private String threadId;
private long totalMessagesReceived = 0;
private long totalMessagesDeleted = 0;
private long totalMessagesRevisibled = 0;
private long totalOutOfOrderMessages = 0;
private long lastMessageReceivedTime = 0;
private boolean continueThread = true;
private AbstractDurablePersistence persistence;
private static final int visibilityTimeout = 600;
private Set<String> messageIds = new HashSet<String>();
//private List<Integer> deleteLatencyMSList = new ArrayList<Integer>();
//private Set<Long> flightTimeList = new HashSet<Long>();
private long emptyResponseCount = 0;
public Receiver(String queueUrl, int index, AbstractDurablePersistence persistence) {
setQueueUrl(queueUrl);
setThreadId(queueUrl, index);
setPersistence(persistence);
}
public String getQueueUrl() {
return queueUrl;
}
public void setQueueUrl(String queueUrl) {
this.queueUrl = queueUrl;
}
public void run() {
try {
CMBControllerServlet.valueAccumulator.initializeAllCounters();
} catch (Exception ex) {
logger.info("Thread=" + threadId + " Action=setup status=exception ", ex);
}
int maxReceiveBatchSize = CQSStressTestProperties.getInstance().getReceiveMessageBatchSize();
try {
int emptyCount = 0;
while (true) {
long startNanoTime = System.currentTimeMillis();
List<CQSMessage> messageList = receiveMessage(this.queueUrl, maxReceiveBatchSize, visibilityTimeout);
if (messageList == null) {
Thread.sleep(300);
continue;
}
long currentTime = System.currentTimeMillis();
long messageFlightTime = 0;
String messageIndex = "";
if (messageList.size() == 0) {
emptyResponseCount++;
logger.info("Thread=" + threadId + " Action=ReceiveMessage batchSize=" + maxReceiveBatchSize + " status=Empty count=" + emptyCount);
if (!isContinueThread()) {
// sleep an extra second if no messages are there to account for potentially hidden messages
Thread.sleep(1000);
emptyCount++;
if (emptyCount > 10) {
logger.info("Thread=" + threadId + "Action=ReceiveMessage status=Completed");
return;
}
}
} else {
emptyCount = 0;
totalMessagesReceived += messageList.size();
logger.info("Thread=" + threadId + " Action=ReceiveMessage batchSize=" + maxReceiveBatchSize + " receivedCount=" + messageList.size());
//addReceiveMessageCount((int)((System.currentTimeMillis() - startTime)/1000), messageList.size());
}
int delayBetweenReceiveAndDelete = CQSStressTestProperties.getInstance().getDelayBetweenReceiveAndDeleteMS();
if (delayBetweenReceiveAndDelete > 0) {
Thread.sleep(delayBetweenReceiveAndDelete + rand.nextInt(40));
}
for (CQSMessage message: messageList) {
String[] bodyParts = message.getBody().split(" ");
for (String bodyPart : bodyParts) {
String[] subParts = bodyPart.split("=");
if (subParts[0].equals("currentTime")) {
long messageSendTime = Long.parseLong(subParts[1]);
messageFlightTime = currentTime - messageSendTime;
if (lastMessageReceivedTime != 0 && lastMessageReceivedTime > messageSendTime) {
totalOutOfOrderMessages++;
//logger.info("Event=MessageOutOfOrder messageId=" + messageIndex + " messageSendTime=" + messageSendTime + " lastMessageReceivedTime=" + lastMessageReceivedTime + " delta=" + Math.abs(lastMessageReceivedTime-messageSendTime));
}
lastMessageReceivedTime = messageSendTime;
} else if (subParts[0].equals("messageIndex")) {
messageIndex = subParts[1];
}
}
if (messageIds.contains(messageIndex)) {
logger.error("Action=receiveMessage status=error exception=Duplicate id: " + messageIndex);
} else {
messageIds.add(messageIndex);
}
//flightTimeList.add(messageFlightTime);
logger.info("Thread=" + threadId + " Action=ReceivedMessage MessageIndex=" + messageIndex + " totalTimeInFlightMillis=" + messageFlightTime);
startNanoTime = System.nanoTime();
if (revisiblePercentage > 0 && rand.nextInt(100)+1 <= revisiblePercentage) {
changeMessageVisibility(this.queueUrl, message.getReceiptHandle());
totalMessagesRevisibled += 1;
totalMessagesReceived -= 1;
messageIds.remove(messageIndex);
long endNanoTime = System.nanoTime();
logger.info("Thread=" + threadId + " Action=RevisibleMessage MessageIndex=" + messageIndex + " latencyNano=" + (endNanoTime-startNanoTime));
} else {
sendMessageIdSet.remove(messageIndex);
deleteMessage(this.queueUrl, message.getReceiptHandle());
totalMessagesDeleted += 1;
long endNanoTime = System.nanoTime();
logger.info("Thread=" + threadId + " Action=DeleteMessage MessageIndex=" + messageIndex + " latencyNano=" + (endNanoTime-startNanoTime));
}
//deleteLatencyMSList.add(new Integer((int)(endNanoTime-startNanoTime)/1000000));
}
int delayBetweenReceivesMS = CQSStressTestProperties.getInstance().getDelayBetweenReceivesMS();
if (delayBetweenReceivesMS > 0) {
try {
Thread.sleep(delayBetweenReceivesMS);
} catch (InterruptedException e) {
logger.error("Action=ReceiveMessage status=Exception ", e);
}
}
}
} catch (AmazonServiceException ase) {
displayServiceException(threadId, "ReceiveMessage/DeleteMessage/RevisibleMessage", ase);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
CMBControllerServlet.valueAccumulator.deleteAllCounters();
}
}
public String getThreadId() {
return threadId;
}
public void setThreadId(String queueUrl, int index) {
this.queueUrl = queueUrl;
if (queueUrl == null || queueUrl.length() == 0) {
return;
}
String queueName = queueUrl.substring(queueUrl.lastIndexOf('/') + 1);
this.threadId = "Receiver_" + queueName + "_" + index;
}
public long getTotalMessagesReceived() {
return totalMessagesReceived;
}
public void setTotalMessagesReceived(long totalMessagesReceived) {
this.totalMessagesReceived = totalMessagesReceived;
}
public long getTotalMessagesDeleted() {
return totalMessagesDeleted;
}
public void setTotalMessagesDeleted(long totalMessagesDeleted) {
this.totalMessagesDeleted = totalMessagesDeleted;
}
public long getTotalMessagesRevisibled() {
return totalMessagesRevisibled;
}
public void setTotalMessagesRevisibled(long totalMessagesRevisibled) {
this.totalMessagesRevisibled = totalMessagesRevisibled;
}
public synchronized boolean isContinueThread() {
return continueThread;
}
public synchronized void setContinueThread(boolean continueThread) {
this.continueThread = continueThread;
}
public AbstractDurablePersistence getPersistence() {
return persistence;
}
public void setPersistence(AbstractDurablePersistence persistence) {
this.persistence = persistence;
}
public long getTotalOutOfOrderMessages() {
return this.totalOutOfOrderMessages;
}
}
private void displayServiceException(String threadId, String action, AmazonServiceException ase) {
logger.error("ThreadId=" + threadId + " Action=" + action);
logger.error("Caught an AmazonServiceException, which means your request made it to Amazon SQS, but was rejected with an error response for some reason.");
logger.error("Error Message=" + ase.getMessage());
logger.error("HTTP Status Code=" + ase.getStatusCode());
logger.error("AWS Error Code=" + ase.getErrorCode());
logger.error("Error Type=" + ase.getErrorType());
logger.error("Request ID=" + ase.getRequestId());
}
public void addReceiveMessageCount(int second, int count) {
AtomicInteger val = null;
timeReceiveMessageCountMap.putIfAbsent(second, new AtomicInteger(0));
val = timeReceiveMessageCountMap.get(second);
val.addAndGet(count);
}
public void addSendMessageCount(int second, int count) {
AtomicInteger val = null;
timeSendMessageCountMap.putIfAbsent(second, new AtomicInteger(0));
val = timeSendMessageCountMap.get(second);
val.addAndGet(count);
}
public String createQueue(String queueName) {
Map<String, String[]> params = new HashMap<String, String[]>();
CommunicationUtils.addParam(params,"Action", "CreateQueue");
CommunicationUtils.addParam(params, "QueueName", queueName);
CommunicationUtils.addParam(params, "AWSAccessKeyId", user.getAccessKey());
CommunicationUtils.addParam(params, "Version", "2009-02-01");
try {
String response = send(params, CMBProperties.getInstance().getCQSServiceUrl());
return CqsStressTester.deserialize(response, "QueueUrl").trim();
} catch (Exception e) {
logger.error("Action=CreateQueue status=error exception=", e);
return null;
}
}
public String sendMessage(String queueUrl, String messageBody) {
Map<String, String[]> params = new HashMap<String, String[]>();
CommunicationUtils.addParam(params,"Action", "SendMessage");
CommunicationUtils.addParam(params, "MessageBody", messageBody);
CommunicationUtils.addParam(params, "AWSAccessKeyId", user.getAccessKey());
CommunicationUtils.addParam(params, "Version", "2009-02-01");
try {
String response = send(params, queueUrl);
String messageId = CqsStressTester.deserialize(response, "MessageId");
return (messageId!=null)?messageId.trim() : null;
} catch (Exception e) {
logger.error("Action=sendMessage status=error exception=", e);
return null;
}
}
public List<CQSMessage> receiveMessage(String queueUrl, int maxNoOfMessages, int visibilityTimeout) {
// Max number of messages will be set to 1 for now
maxNoOfMessages = 1;
Map<String, String[]> params = new HashMap<String, String[]>();
CommunicationUtils.addParam(params,"Action", "ReceiveMessage");
CommunicationUtils.addParam(params, "MaxNumberOfMessages", "" + maxNoOfMessages);
CommunicationUtils.addParam(params, "VisibilityTimeout", "" + visibilityTimeout);
CommunicationUtils.addParam(params, "AWSAccessKeyId", user.getAccessKey());
CommunicationUtils.addParam(params, "Version", "2009-02-01");
try {
long ts1 = System.currentTimeMillis();
String response = send(params, queueUrl);
long elapsedTime = System.currentTimeMillis() - ts1;
logger.info("Total time spent on receiveMessage=" + elapsedTime);
if (response.indexOf("<Body>") > 0) {
//receiveLacencyMSList.add(elapsedTime);
}
return CqsStressTester.deserializeMessage(response);
} catch (Exception e) {
logger.error("Action=receiveMessage status=error exception=", e);
return null;
}
}
public void deleteMessage(String queueUrl, String receiptHandle) {
Map<String, String[]> params = new HashMap<String, String[]>();
CommunicationUtils.addParam(params,"Action", "DeleteMessage");
CommunicationUtils.addParam(params, "ReceiptHandle", receiptHandle);
CommunicationUtils.addParam(params, "AWSAccessKeyId", user.getAccessKey());
CommunicationUtils.addParam(params, "Version", "2009-02-01");
try {
send(params, queueUrl);
} catch (Exception e) {
logger.error("Action=deleteMessage status=error exception=", e);
}
}
public void changeMessageVisibility(String queueUrl, String receiptHandle) {
Map<String, String[]> params = new HashMap<String, String[]>();
CommunicationUtils.addParam(params,"Action", "ChangeMessageVisibility");
CommunicationUtils.addParam(params, "ReceiptHandle", receiptHandle);
int randomVisibilityTimeoutSecs = rand.nextInt(5);
CommunicationUtils.addParam(params, "VisibilityTimeout", randomVisibilityTimeoutSecs+"");
CommunicationUtils.addParam(params, "AWSAccessKeyId", user.getAccessKey());
CommunicationUtils.addParam(params, "Version", "2009-02-01");
try {
send(params, queueUrl);
} catch (Exception e) {
logger.error("Action=changeMessageVisibilityTimeout status=error exception=", e);
}
}
public String send(Map<String, String[]> params, String endPoint) throws Exception {
logger.debug("event=send_cqs_message endpoint=" + endPoint);
String url = endPoint;
logger.debug("event=send_cqs_message url=" + url + " endpoint=" + endPoint+ "\"");
Set<String> parameters = params.keySet();
boolean first = true;
for (String param: parameters) {
if(first) {url += "?"; first=false;}
else url += "&";
url += URLEncoder.encode(param,"UTF-8") + "=" + URLEncoder.encode(params.get(param)[0], "UTF-8");
}
String resp = "";
try {
logger.debug("Sending request to url:" + url);
//resp = sendHttpMessage(url);
resp = send(url, "");
} catch(Exception e) {
logger.error("event=send_cqs_message endpoint=" + endPoint + " exception=" + e.toString(), e);
throw new CMBException(CQSErrorCodes.InternalError, "internal service error");
}
return resp;
}
public String send(String endpoint, String message) throws Exception {
logger.debug("event=send_http_request endpoint=" + endpoint + "\" message=\"" + message + "\"");
if ((message == null) || (endpoint == null)) {
logger.debug("event=send_http_request error_code=MissingParameters endpoint=" + endpoint + "\" message=\"" + message + "\"");
throw new Exception("Message and Endpoint must both be set");
}
HttpPost httpPost = new HttpPost(endpoint);
StringEntity stringEntity = new StringEntity(message);
httpPost.setEntity(stringEntity);
HttpResponse response = httpClient.execute(httpPost);
response.getStatusLine().getStatusCode();
HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream instream = entity.getContent();
InputStreamReader responseReader = new InputStreamReader(instream);
StringBuffer responseB = new StringBuffer();
char []arr = new char[1024];
int size = 0;
while ((size = responseReader.read(arr, 0, arr.length)) != -1) {
responseB.append(arr, 0, size);
}
instream.close();
return responseB.toString();
}
logger.error("Could not get response entity");
System.exit(1);
return null;
}
public static List<CQSMessage> deserializeMessage(String serializedMessage) {
javax.xml.parsers.SAXParserFactory fac = new org.apache.xerces.jaxp.SAXParserFactoryImpl();
javax.xml.parsers.SAXParser saxParser;
SaxHandler p = new SaxHandler();
try {
saxParser = fac.newSAXParser();
saxParser.parse(new ByteArrayInputStream(serializedMessage.getBytes()), p);
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
CQSMessage cqsMessage = null;
try {
if (p.getValueByKey("Body") == null) {
return new ArrayList<CQSMessage>();
}
cqsMessage = new CQSMessage(p.getValueByKey("Body"), p.getAttributes());
cqsMessage.setMessageId(p.getValueByKey("MessageId"));
cqsMessage.setReceiptHandle(p.getValueByKey("ReceiptHandle"));
cqsMessage.setMD5OfBody(p.getValueByKey("MD5OfBody"));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return java.util.Arrays.asList(cqsMessage) ;
}
public static String deserialize(String serialized, String key) {
javax.xml.parsers.SAXParserFactory fac = new org.apache.xerces.jaxp.SAXParserFactoryImpl();
javax.xml.parsers.SAXParser saxParser;
SaxHandler p = new SaxHandler();
try {
saxParser = fac.newSAXParser();
saxParser.parse(new ByteArrayInputStream(serialized.getBytes()), p);
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return p.getValueByKey(key);
}
private static class SaxHandler extends org.xml.sax.helpers.DefaultHandler {
private CharArrayWriter contents = new CharArrayWriter();
private HashMap<String, String> messageMap = new HashMap<String, String>();
private String _name;
private String _value;
private HashMap<String, String> messageAttributes = new HashMap<String, String>();
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
// clear the contents since we are in a new tag
if (qName.equals("Message")) {
messageMap.clear();
messageAttributes.clear();
}
contents.reset();
}
@Override
public void endElement(String uri, String localName, String qName) {
if (qName.equals("Name")) {
_name = contents.toString();
} else if (qName.equals("Value")) {
_value = contents.toString();
} else if (qName.equals("Attribute")) {
messageAttributes.put(_name, _value);
} else if (!qName.equals("Message") && !qName.contains("Response") && !qName.contains("Result") && !qName.equals("RequestId")){
messageMap.put(qName, contents.toString());
}
}
@Override
public void characters(char[] ch, int start, int length) {
contents.write(ch, start, length);
}
String getValueByKey(String k) {
return messageMap.get(k);
}
HashMap<String, String> getAttributes() {
return messageAttributes;
}
}
}