在多个线程上使用AmazonSQS sendMessage会导致其运行速度变慢

时间:2017-08-15 18:04:27

标签: java multithreading amazon-web-services amazon-ec2 amazon-sqs

我有一个应用程序可以将简单的SQS消息发送到多个队列。以前,这个发送是连续发生的,但是现在我们需要发送更多队列,我决定通过在线程池(最多10个线程)中进行所有发送来并行化它。

但是,我注意到当我在工作中抛出更多线程时,sqs.sendMessage延迟似乎会增加!

我在下面创建了一个示例程序来重现问题(请注意numIterations只是为了获取更多数据,这只是用于演示目的的代码的简化版本)。

在相同区域的EC2实例上运行并使用7个队列,我通常得到平均结果大约12-15ms,1个线程,21-25ms,7个线程 - 几乎是延迟的两倍!

即使从我的笔记本电脑远程运行(创建此演示时),我的平均延迟时间约为90毫秒,1个线程,约120毫秒,7个线程。

public static void main(String[] args) throws Exception {
    AWSCredentialsProvider creds = new AWSStaticCredentialsProvider(new BasicAWSCredentials(A, B));
    final int numThreads = 7;
    final int numQueues = 7;
    final int numIterations = 100;
    final long sleepMs = 10000;

    AmazonSQSClient sqs = new AmazonSQSClient(creds);
    List<String> queueUrls = new ArrayList<>();
    for (int i=0; i<numQueues; i++) {
        queueUrls.add(sqs.getQueueUrl("testThreading-" + i).getQueueUrl());    
    }

    Queue<Long> resultQueue = new ConcurrentLinkedQueue<>(); 
    sqs.addRequestHandler(new MyRequestHandler(resultQueue));

    runIterations(sqs, queueUrls, numThreads, numIterations, sleepMs);
    System.out.println("Average: " + resultQueue.stream().mapToLong(Long::longValue).average().getAsDouble());
    System.exit(0);
}

private static void runIterations(AmazonSQS sqs, List<String> queueUrls, int threadPoolSize, int numIterations, long sleepMs) throws Exception {
    ExecutorService executor = Executors.newFixedThreadPool(threadPoolSize);
    List<Future<?>> futures = new ArrayList<>();
    for (int i=0; i<numIterations; i++) {
        for (String queueUrl : queueUrls) {
            final String message = String.valueOf(i);
            futures.add(executor.submit(() -> sendMessage(sqs, queueUrl, message)));
        }
        Thread.sleep(sleepMs);
    }

    for (Future<?> f : futures) {
        f.get();
    }

}

private static void sendMessage(AmazonSQS sqs, String queueUrl, String messageBody) {
    final SendMessageRequest request = new SendMessageRequest()
            .withQueueUrl(queueUrl)
            .withMessageBody(messageBody);
    sqs.sendMessage(request);
}

// Use RequestHandler2 to get accurate timing metrics
private static class MyRequestHandler extends RequestHandler2 {
    private final Queue<Long> resultQueue;
    public MyRequestHandler(Queue<Long> resultQueue) {
        this.resultQueue = resultQueue;
    }

    public void afterResponse(Request<?> request, Response<?> response) {
        TimingInfo timingInfo = request.getAWSRequestMetrics().getTimingInfo();
        Long start = timingInfo.getStartEpochTimeMilliIfKnown();
        Long end = timingInfo.getEndEpochTimeMilliIfKnown();
        if (start != null && end != null) {
            long elapsed = end-start;
            resultQueue.add(elapsed);
        }
    }
}

我确定这是一些奇怪的客户端配置问题,但默认的ClientConfiguration应该能够处理50个并发连接。

有什么建议吗?

更新:看起来这个问题的关键是我遗漏了原始简化版本的问题 - 发送的批次邮件之间存在延迟(与处理相关)。如果延迟是~2s,则延迟问题不存在,但当批次之间的延迟为~10s时, 是个问题。我为ClientConfiguration.validateAfterInactivityMillis尝试了不同的值而没有效果。

0 个答案:

没有答案