1

We're currently in the middle of migrating our current architecture into Spring-AWS-based microservices. One of my tasks is to research on how our microservices communicate with one another. I'm aiming to set-up a hybrid system of RESTful HTTP endpoints and SQS producers and consumers.

As an example, I have the below code:

@SqsListener("request_queue")
@SendTo("response_queue")
@PostMapping("/send")
public Object send(@RequestBody Request request, @Header("SenderId") String senderId) {
    if (senderId != null && !senderId.trim().isEmpty()) {
        logger.info("SQS Message Received!");
        logger.info("Sender ID: ".concat(senderId));
        request = new Gson().fromJson(payload, Request.class);
    }

    Response response = processRequest(request); // Process request

    return response;
}

Theoretically, this method should be able to handle the following:

  1. Receive a Request object via HTTP
  2. Continually poll the request_queue for a message containing the Request object

As an HTTP endpoint, the method returns no error. However, as an SQS listener, it runs into the following exception:

org.springframework.messaging.converter.MessageConversionException: 
    Cannot convert from [java.lang.String] to [com.oriente.salt.Request] for 
    GenericMessage [payload={"source":"QueueTester","message":"This is a wonderful 
    message send by queue from Habanero to Salt. Spicy.","msisdn":"+639772108550"}, 
    headers={LogicalResourceId=salt_queue, ApproximateReceiveCount=1, 
    SentTimestamp=1523444620218, ....

I've tried to annotate the Request param with @Payload, but to no avail. Currently I've also set-up the AWS config via Java, as seen below:

ConsuerAWSSQSConfig.java

@Configuration
public class ConsumerAWSSQSConfig {

@Bean
public SimpleMessageListenerContainer simpleMessageListenerContainer() {
    SimpleMessageListenerContainer msgListenerContainer = simpleMessageListenerContainerFactory()
            .createSimpleMessageListenerContainer();
    msgListenerContainer.setMessageHandler(queueMessageHandler());
    return msgListenerContainer;
}

@Bean
public SimpleMessageListenerContainerFactory simpleMessageListenerContainerFactory() {
    SimpleMessageListenerContainerFactory msgListenerContainerFactory = new SimpleMessageListenerContainerFactory();
    msgListenerContainerFactory.setAmazonSqs(amazonSQSClient());
    return msgListenerContainerFactory;
}

@Bean
public QueueMessageHandler queueMessageHandler() {
    QueueMessageHandlerFactory queueMsgHandlerFactory = new QueueMessageHandlerFactory();
    queueMsgHandlerFactory.setAmazonSqs(amazonSQSClient());
    QueueMessageHandler queueMessageHandler = queueMsgHandlerFactory.createQueueMessageHandler();
    List<HandlerMethodArgumentResolver> list = new ArrayList<>();
    HandlerMethodArgumentResolver resolver = new PayloadArgumentResolver(new MappingJackson2MessageConverter());
    list.add(resolver);
    queueMessageHandler.setArgumentResolvers(list);
    return queueMessageHandler;
}

@Lazy
@Bean(name = "amazonSQS", destroyMethod = "shutdown")
public AmazonSQSAsync amazonSQSClient() {
    AmazonSQSAsync awsSQSAsync = AmazonSQSAsyncClientBuilder.standard().withRegion(Regions.AP_SOUTHEAST_1).build();
    return awsSQSAsync;
}
}

What do you guys think?

  • 1
    Why? Have two ingress points, you are trying to combine very different technologies with the assumption springs blackmagic will always handle this.. The Queue and the HTTP routes and just route them through a shared component/service. What if your HTTP API ever needs to change? – Darren Forsythe Apr 11 '18 at 11:46

0 Answers0