Spring Cloud SQS消耗阻塞,直到处理完所有消息

时间:2016-08-05 04:00:50

标签: java spring spring-boot spring-cloud amazon-sqs

我们正在使用Spring Cloud AWS与SQS进行互动。我们使用@SqsListener注释从我们的队列中提取消息。我们有deletionPolicy = NEVER,这意味着我们手动确认我们选择的所有邮件。

我们的问题是SimpleMessageListenerContainer(处理来自队列的消息处理)等待所有工作线程完成,然后再从队列中选择更多消息。

换句话说,我们所看到的是:

  • 从队列中拉出10条消息。
  • 启动10个线程来完成工作。
  • 正在进行工作的其中一个线程在缓慢的IO调用中被阻止。
  • 现在阻止应用程序从队列中取出更多消息,并且因此阻止其完成更多工作,直到缓慢的呼叫结束。

我们可以看到SimpleMessageListenerContainer.AsynchronousMessageListener中负责此

的代码
@Override
public void run() {
    while (isQueueRunning()) {
        try {
            ReceiveMessageResult receiveMessageResult = getAmazonSqs().receiveMessage(this.queueAttributes.getReceiveMessageRequest());
            CountDownLatch messageBatchLatch = new CountDownLatch(receiveMessageResult.getMessages().size());
            for (Message message : receiveMessageResult.getMessages()) {
                if (isQueueRunning()) {
                    MessageExecutor messageExecutor = new MessageExecutor(this.logicalQueueName, message, this.queueAttributes);
                    getTaskExecutor().execute(new SignalExecutingRunnable(messageBatchLatch, messageExecutor));
                } else {
                    messageBatchLatch.countDown();
                }
            }
            try {
                messageBatchLatch.await();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        } catch (Exception e) {
            getLogger().warn("An Exception occurred while polling queue '{}'. The failing operation will be " +
                    "retried in {} milliseconds", this.logicalQueueName, getBackOffTime(), e);
            try {
                //noinspection BusyWait
                Thread.sleep(getBackOffTime());
            } catch (InterruptedException ie) {
                Thread.currentThread().interrupt();
            }
        }
    }
}

理想情况下,我们希望消息监听器不断从队列中选择消息进行处理。

我们似乎无法实现自己的MessageListenerContainer,因为AbstractMessageListenerContainer是本地包。

这种行为有什么办法吗?

1 个答案:

答案 0 :(得分:0)

持有消息轮询线程的是messageBatchLatch.await()语句。似乎只需移除闩锁即可。类似的东西:

@Override
public void run() {
    while (isQueueRunning()) {
        try {
            ReceiveMessageResult receiveMessageResult = getAmazonSqs().receiveMessage(this.queueAttributes.getReceiveMessageRequest());
            for (Message message : receiveMessageResult.getMessages()) {
                if (isQueueRunning()) {
                    MessageExecutor messageExecutor = new MessageExecutor(this.logicalQueueName, message, this.queueAttributes);
                    getTaskExecutor().execute(new SignalExecutingRunnable(messageExecutor));
                }
            }
        } catch (Exception e) {
            getLogger().warn("An Exception occurred while polling queue '{}'. The failing operation will be " +
                    "retried in {} milliseconds", this.logicalQueueName, getBackOffTime(), e);
            try {
                //noinspection BusyWait
                Thread.sleep(getBackOffTime());
            } catch (InterruptedException ie) {
                Thread.currentThread().interrupt();
            }
        }
    }
}

如果您的TaskExecutor实施,这将有效: - 有一个固定大小的线程池 - 调用execute函数且没有线程可用时阻塞。

这是大多数实现的工作方式,但值得检查一下。