简体   繁体   中英

Listen message queue SQS with Spring Boot not works with standard config

I'm unable to make works queue listener with Spring Boot and SQS (the message is sent and appear in SQS ui)

The @MessageMapping or @SqsListener not works

Java: 11
Spring Boot: 2.1.7
Dependencie: spring-cloud-aws-messaging

This is my config

@Configuration
@EnableSqs
public class SqsConfig {

    @Value("#{'${env.name:DEV}'}")
    private String envName;

    @Value("${cloud.aws.region.static}")
    private String region;

    @Value("${cloud.aws.credentials.access-key}")
    private String awsAccessKey;

    @Value("${cloud.aws.credentials.secret-key}")
    private String awsSecretKey;

    @Bean
    public Headers headers() {
        return new Headers();
    }

    @Bean
    public MessageQueue queueMessagingSqs(Headers headers,
                                          QueueMessagingTemplate queueMessagingTemplate) {
        Sqs queue = new Sqs();
        queue.setQueueMessagingTemplate(queueMessagingTemplate);
        queue.setHeaders(headers);
        return queue;
    }

    private ResourceIdResolver getResourceIdResolver() {
        return queueName -> envName + "-" + queueName;
    }

    @Bean
    public DestinationResolver destinationResolver(AmazonSQSAsync amazonSQSAsync) {
        DynamicQueueUrlDestinationResolver destinationResolver = new DynamicQueueUrlDestinationResolver(
                amazonSQSAsync,
                getResourceIdResolver());
        destinationResolver.setAutoCreate(true);
        return destinationResolver;
    }

    @Bean
    public QueueMessagingTemplate queueMessagingTemplate(AmazonSQSAsync amazonSQSAsync,
                                                         DestinationResolver destinationResolver) {
        return new QueueMessagingTemplate(amazonSQSAsync, destinationResolver, null);
    }

    @Bean
    public QueueMessageHandlerFactory queueMessageHandlerFactory() {
        QueueMessageHandlerFactory factory = new QueueMessageHandlerFactory();
        MappingJackson2MessageConverter messageConverter = new MappingJackson2MessageConverter();
        messageConverter.setStrictContentTypeMatch(false);
        factory.setArgumentResolvers(Collections.singletonList(new PayloadArgumentResolver(messageConverter)));
        return factory;
    }

    @Bean
    public SimpleMessageListenerContainerFactory simpleMessageListenerContainerFactory(AmazonSQSAsync amazonSqs) {
        SimpleMessageListenerContainerFactory factory = new SimpleMessageListenerContainerFactory();
        factory.setAmazonSqs(amazonSqs);
        factory.setMaxNumberOfMessages(10);
        factory.setWaitTimeOut(2);
        return factory;
    }

}

I notice also that org.springframework.cloud.aws.messaging.config.SimpleMessageListenerContainerFactory and org.springframework.cloud.aws.messaging.config.annotation.SqsConfiguration run on startup

And my test

@RunWith(SpringJUnit4ClassRunner.class)
public class ListenTest {

    @Autowired
    private MessageQueue queue;

    private final String queueName = "test-queue-receive";

    private String result = null;

    @Test
    public void test_listen() {
        // given
        String data = "abc";

        // when
        queue.send(queueName, data).join();

        // then
        Awaitility.await()
                .atMost(10, TimeUnit.SECONDS)
                .until(() -> Objects.nonNull(result));

        Assertions.assertThat(result).equals(data);
    }

    @MessageMapping(value = queueName)
    public void receive(String data) {
        this.result = data;
    }
}

Do you think something is wrong ?

I create a repo for exemple : ( https://github.com/mmaryo/java-sqs-test )
In test folder, change aws credentials in 'application.yml'
Then run tests

I had the same issue when using the spring-cloud-aws-messaging package, but then I used the queue URL in the @SqsListener annotation instead of the queue name and it worked.

@SqsListener(value = { "https://full-queue-URL" }, deletionPolicy = SqsMessageDeletionPolicy.ON_SUCCESS)
public void receive(String message) {
     // do something
}

It seems you can use the queue name when using the spring-cloud-starter-aws-messaging package. I believe there is some configuration that allows usage of the queue name instead of URL if you don't want to use the starter package.

EDIT: I noticed the region was being defaulted to us-west-2 despite me listing us-east-1 in my properties file. Then I created a RegionProvider bean and set the region to us-east-1 in there and now when I use the queue name in the @SqsMessaging it is found and correctly resolved to the URL in the framework code.

you'll need to leverage the @Primary annotation, this is what worked for me:

@Autowired(required = false)
private AWSCredentialsProvider awsCredentialsProvider;

@Autowired
private AppConfig appConfig;

@Bean
public QueueMessagingTemplate getQueueMessagingTemplate() {
    return new QueueMessagingTemplate(sqsClient());
}

@Primary
@Bean
public AmazonSQSAsync sqsClient() {
    AmazonSQSAsyncClientBuilder builder = AmazonSQSAsyncClientBuilder.standard();

    if (this.awsCredentialsProvider != null) {
        builder.withCredentials(this.awsCredentialsProvider);
    }

    if (appConfig.getSqsRegion() != null) {
        builder.withRegion(appConfig.getSqsRegion());
    } else {
        builder.withRegion(Regions.DEFAULT_REGION);
    }

    return builder.build();
}

build.gradle needs these deps:

implementation("org.springframework.cloud:spring-cloud-starter-aws:2.2.0.RELEASE")
implementation("org.springframework.cloud:spring-cloud-aws-messaging:2.2.0.RELEASE")

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM