Java 带线程池执行器的SQSListener
在下面的示例中,我将最大和核心池大小设置为1。但是,未处理任何消息。当我启用调试日志时,我能够看到从SQS中提取的消息,但我猜它没有被处理/删除。但是,当我将core和max pool size增加到2时,消息似乎已被处理 编辑 我相信Spring可能会为从队列中读取数据的接收方分配一个线程,因此它无法将线程分配给正在处理消息的侦听器。当我将corepoolsize增加到2时,我看到消息正在从队列中读取。当我添加另一个侦听器(用于死信队列)时,我遇到了相同的问题-2个线程不够,因为消息没有被处理。当我将corepoolsize增加到3时,它开始处理消息。我假设在本例中,分配了1个线程从队列中读取消息,并为2个侦听器分别分配了1个线程Java 带线程池执行器的SQSListener,java,spring,amazon-web-services,spring-cloud,amazon-sqs,Java,Spring,Amazon Web Services,Spring Cloud,Amazon Sqs,在下面的示例中,我将最大和核心池大小设置为1。但是,未处理任何消息。当我启用调试日志时,我能够看到从SQS中提取的消息,但我猜它没有被处理/删除。但是,当我将core和max pool size增加到2时,消息似乎已被处理 编辑 我相信Spring可能会为从队列中读取数据的接收方分配一个线程,因此它无法将线程分配给正在处理消息的侦听器。当我将corepoolsize增加到2时,我看到消息正在从队列中读取。当我添加另一个侦听器(用于死信队列)时,我遇到了相同的问题-2个线程不够,因为消息没有被处理
@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);
}
通过将
corePoolSize
和maximumPoolSize
设置相同,可以创建固定大小的线程池。记录了对规则的非常好的解释
设置maxPoolSize
隐式允许删除任务。
但是,默认队列容量是整数.MAX_值
,实际上,它是无穷大的
需要注意的是,ThreadPoolTaskExecutor
在下面使用了一个ThreadPoolExecutor
,它有一种不寻常的排队方法,如中所述:
如果corePoolSize
或多个线程正在运行,那么执行器总是倾向于将请求排队,而不是添加新线程
这意味着maxPoolSize
仅在队列已满时才相关,否则线程数将永远不会超过corePoolSize
。
例如,如果我们向线程池提交从未完成的任务:
- 第一次提交的
corePoolSize
将分别启动一个新线程李>
- 之后,所有提交的文件都进入队列李>
- 如果队列是有限的,且其容量已耗尽,则每次提交都会启动一个新线程,最大可达
maxPoolSize
李>
- 当池和队列都已满时,新提交将被拒绝
排队-阅读
任何BlockingQueue
都可用于传输和保存提交的任务。此队列的使用与池大小交互:
- 如果运行的线程少于corePoolSize,则执行器始终
更喜欢添加新线程而不是排队李>
- 如果corePoolSize或多个线程正在运行,则执行器始终
更喜欢对请求排队,而不是添加新线程李>
- 如果请求无法排队,则会创建一个新线程,除非
这将超过maximumPoolSize,在这种情况下,任务将
被拒绝
无限队列
。使用无界队列(例如
LinkedBlockingQueue
没有预定义的容量)将导致新的
在所有corePoolSize线程都繁忙的情况下要排队的任务。
因此,不会创建超过corePoolSize
的线程。(及
因此,maximumPoolSize
的值没有任何影响。)
如果线程数小于corePoolSize
,则创建一个新的
线程以运行新任务
如果线程数等于(或大于)
corePoolSize
,将任务放入队列
如果队列已满,且线程数小于
maxPoolSize
,创建一个新线程以在其中运行任务
如果队列已满,且线程数大于或
等于maxPoolSize
,拒绝该任务
谢谢@Vikram Palakurthi。在我的示例中,我没有设置队列容量。所以我相信它是无限的。因此,我假设的maxpoolsize不相关。我尝试将大约10条消息推送到SQS FIFO队列。因为corepoolsize设置为1,所以我假设使用者将逐个处理消息。但是消费者没有获取消息。但是,当我将其设置为2时,消息正在被读取。是否有一个线程始终专用于轮询消息,因此如果我将队列大小设置为2,则第二条消息正在进行处理?否,不适用于轮询,但我猜在您的示例中是executor.setCorePoolSize(1);执行器setMaxPoolSize(1);第四条规则正在应用,任务将被拒绝。如果您看到规则的顺序,我猜执行器会检测到线程大于maxPoolSize,即1,并拒绝任务,这解释了为什么将其增加到2时处理工作正常。通过将corePoolSize和maximumPoolSize设置为相同,您创建了一个固定大小的线程池,让它无法动态分配不断增长的线程。尝试删除此部件执行器。setMaxPoolSize(1);我想只有当队列容量达到时,maxpoolsize才会生效。这甚至在您的回答中也有说明-“这意味着maxPoolSize仅在队列已满时才相关”。在我的情况下,我只是向队列发送4-5条消息以进行测试。我还尝试将maxpoolsize增加到1。但这种配置不会产生任何影响。只有当我将core pool size增加到大于1时,它才起作用。正确,在您的情况下core pool size为1,因为您有大约4-5条消息,所以maxPoolSize将生效。因为您将corePoolSize和maxPoolSize都设为1,这导致了问题。您提到您尝试将maxPoolSize增加到1,但这并不能解决您的问题,尝试将其增加到任何大于1的值,如2-5。这是我为这个问题创建的票证-