带有ThreadpoolExecutor的SQSListener

时间:2018-04-27 21:13:17

标签: java spring amazon-web-services spring-cloud amazon-sqs

在下面的示例中,我将max和core pool size设置为1.但是没有消息正在处理中。当我启用调试日志时,我能够看到从SQS中提取的消息,但我猜它没有被处理/删除。但是,当我将核心和最大池大小增加到2时,似乎会处理消息。

修改

我相信Spring可能为接收器分配一个线程,该接收器从队列中读取数据,因此无法将线程分配给正在处理消息的侦听器。当我将corepoolsize增加到2时,我看到消息正在从队列中读取。当我添加另一个监听器(用于死信队列)时,我遇到了同样的问题 - 由于没有处理消息,因此2个线程是不够的。当我将corepoolsize增加到3时,它开始处理消息。我假设在这种情况下,分配了1个线程来读取队列中的消息,并为2个侦听器分配了1个线程。

@Configuration
public class SqsListenerConfiguration {

    @Bean
    @ConfigurationProperties(prefix = "aws.configuration")
    public ClientConfiguration clientConfiguration() {
        return new ClientConfiguration();
    }


    @Bean
    @Primary
    public AWSCredentialsProvider awsCredentialsProvider() {

        ProfileCredentialsProvider credentialsProvider = new ProfileCredentialsProvider("credential");
        try {
            credentialsProvider.getCredentials();
            System.out.println(credentialsProvider.getCredentials().getAWSAccessKeyId());
            System.out.println(credentialsProvider.getCredentials().getAWSSecretKey());

        } catch (Exception e) {
            throw new AmazonClientException(
                    "Cannot load the credentials from the credential profiles file. " +
                            "Please make sure that your credentials file is at the correct " +
                            "location (~/.aws/credentials), and is in valid format.",
                    e);
        }
        return credentialsProvider;
    }


    @Bean
    @Primary
    public AmazonSQSAsync amazonSQSAsync() {
        return AmazonSQSAsyncClientBuilder.standard().
                withCredentials(awsCredentialsProvider()).
                withClientConfiguration(clientConfiguration()).
                build();
    }


    @Bean
    @ConfigurationProperties(prefix = "aws.queue")
    public SimpleMessageListenerContainer simpleMessageListenerContainer(AmazonSQSAsync amazonSQSAsync) {
        SimpleMessageListenerContainer simpleMessageListenerContainer = new SimpleMessageListenerContainer();
        simpleMessageListenerContainer.setAmazonSqs(amazonSQSAsync);
        simpleMessageListenerContainer.setMessageHandler(queueMessageHandler());
        simpleMessageListenerContainer.setMaxNumberOfMessages(10);
        simpleMessageListenerContainer.setTaskExecutor(threadPoolTaskExecutor());
        return simpleMessageListenerContainer;
    }


    @Bean
    public QueueMessageHandler queueMessageHandler() {
        QueueMessageHandlerFactory queueMessageHandlerFactory = new QueueMessageHandlerFactory();
        queueMessageHandlerFactory.setAmazonSqs(amazonSQSAsync());
        QueueMessageHandler queueMessageHandler = queueMessageHandlerFactory.createQueueMessageHandler();
        return queueMessageHandler;
    }


    @Bean
    public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(1);
        executor.setMaxPoolSize(1);
        executor.setThreadNamePrefix("oaoQueueExecutor");
        executor.initialize();
        return executor;
    }


    @Bean
    public QueueMessagingTemplate messagingTemplate(@Autowired AmazonSQSAsync amazonSQSAsync) {
        return new QueueMessagingTemplate(amazonSQSAsync);
    }


}

监听器配置

    @SqsListener(value = "${oao.sqs.url}", deletionPolicy = SqsMessageDeletionPolicy.ON_SUCCESS)
    public void onMessage(String serviceData, @Header("MessageId") String messageId, @Header("ApproximateFirstReceiveTimestamp") String approximateFirstReceiveTimestamp) {

        System.out.println(" Data = " + serviceData + " MessageId = " + messageId);

        repository.execute(serviceData);
}

1 个答案:

答案 0 :(得分:6)

通过设置corePoolSizemaximumPoolSize相同,您可以创建fixed-size thread pool。记录了here

对规则的非常好的解释

设置maxPoolSize隐式允许删除任务。 但是,默认队列容量为Integer.MAX_VALUE,出于实际目的,该容量为无穷大。

值得注意的是ThreadPoolTaskExecutor下面使用ThreadPoolExecutor,这有一种不同寻常的排队方法,在the docs中有描述:

  

如果corePoolSize个或更多线程正在运行,则执行程序总是更喜欢排队请求而不是添加新线程。

这意味着maxPoolSize仅在队列已满时才相关,否则线程数将永远不会超过corePoolSize。 例如,如果我们向线程池提交永远不会完成的任务:

  • 第一个corePoolSize提交将分别开始一个新主题;
  • 之后,所有提交都进入队列;
  • 如果队列是有限的并且其容量已用尽,则每次提交都会启动一个新线程,最多为maxPoolSize;
  • 当池和队列都已满时,新的提交将被拒绝。

排队 - 阅读docs

任何BlockingQueue都可用于转移和保留已提交的任务。此队列的使用与池大小调整相互作用:

  • 如果正在运行少于corePoolSize的线程,则始终执行Executor 喜欢添加新线程而不是排队。
  • 如果正在运行corePoolSize或更多线程,则始终执行Executor 更喜欢排队请求而不是添加新线程。
  • 如果请求无法排队,则创建新线程,除非 这将超过maximumPoolSize,在这种情况下,任务将会 被拒绝。
  

Unbounded queues。使用无界队列(例如a   没有预定义容量的LinkedBlockingQueue将导致新的   在所有corePoolSize线程都忙的情况下排队的任务。   因此,永远不会创建corePoolSize个线程。 (而且   因此maximumPoolSize的值没有任何影响。)

  1. 如果线程数小于corePoolSize,请创建一个新线程 线程来运行一个新任务。
  2. 如果线程数等于(或大于) corePoolSize,将任务放入队列。
  3. 如果队列已满,且线程数小于 maxPoolSize,创建一个新线程来运行任务。
  4. 如果队列已满,且线程数大于或 等于maxPoolSize,拒绝任务。