Spring Boot became ubiquitous in recent years and provided an opinionated way of integrating various pieces of technology. Working with JMS is no exception to that. Although Amazon has its own Java API for interacting with SQS, using it through JMS ensures that we’ll be able to use the same piece of code with another messaging infrastructure. After taking a look at a basic message consumer and producer setup, we dive into a more advanced use case: consuming large messages.
Working Example
If you’re like most people, perhaps you’d like to see the big picture first and go into the details after that. I prepared a fully working example, which is available on GitHub, and here:
% git clone git@github.com:springuni/springuni-examples.git
% cd springuni-examples
Create a local configuration file .env with the following contents in the root of the project (springuni-examples).
After starting the demo app, it will listen on port 5000. I would have liked to keep it simple and it just handles bare text messages. Spring’s automated message converter infrastructure doesn’t play a role here, as the article’s focus is how to deal with large SQS messages.
If everything went well, you should see the following messages:
2017-08-09 19:13:01.443 INFO 29525 --- [nio-5000-exec-1] c.s.examples.jms.MessageProducer : Sending message test.
2017-08-09 19:13:02.069 INFO 29525 --- [nio-5000-exec-1] c.a.s.javamessaging.SQSMessageProducer : Message sent to SQS with SQS-assigned messageId: 1001f7ba-55fc-4bdb-8732-8f4d40343068
2017-08-09 19:13:02.069 INFO 29525 --- [nio-5000-exec-1] com.amazon.sqs.javamessaging.SQSSession : Shutting down SessionCallBackScheduler executor
2017-08-09 19:13:02.243 INFO 29525 --- [enerContainer-1] c.s.examples.jms.MessageConsumer : Received message test
I’m hoping you’re still with me and interested in seeing the details.
Nuances of Working With SQS
SQS is an odd one out from the point of view of how message brokers operate in general.
SQS Maximizes the Size of Messages at 256K
Exchanging data in larger chunks than that can be achieved in various ways. When I first faced this limitation, I applied gzip compression and encoded the compressed binary data with base64. That solution worked just fine for textual (JSON) data, however, it required customization on both the producer’s and the consumer’s side. Furthermore, compression itself doesn’t guarantee that the size of messages to be sent will never exceed the 256K limit. Amazon SDK provides an extended SQS client, which enables end users to exchange messages larger than 256K transparently without having to apply customization themselves. The extended SQS client leverages S3 for storing messages and only a reference to the stored object is being sent to queues.
Annotation Driven Message Listener Configuration Requires the Queue Name to Be Defined Up Front
Arbitrary methods of a managed bean can be annotated with @JmsListener(destination = "queueName") or alternatively javax.jms.MessageListener can be implemented instead. Nevertheless, going for the first option is much more convenient as Spring intelligently converts the received message to various user-defined data type through its MessageConverter infrastructure.
In SQS, endpoint URLs identify queues and they also contain the queue’s name. Such a URL looks like this: https://sqs.<region>.amazonaws.com/<acctount- id>/<queue-name>.
Obviously, we can extract the queue name from an URL like this, however, the way JMS can be set up with Spring Boot requires you to define the queue’s name directly.
In order to be able to leverage Spring’s messaging infrastructure without having to hard code a JMS destination in the message consumer or having to repeat the queue’s name in the application’s configuration, we need to implement a custom DestinationResolver. That DestinationResolver will eventually parse the endpoints URL of an SQS queue and we’ll have to fiddle with only a single application property.
Basic Setup With SQS
We continue from that point where Messaging with JMS left off. That guide doesn’t configure ConnectionFactory explicitly, in which case Spring Boot configures an embedded ActiveMQ broker.
Maven Dependencies
To showcase how Amazon’s JMS messaging library plays with Spring, we need to set up the following dependencies.
Basically, amazon-sqs-java-extended-client-lib is only required if you would like to send large messages and 256K might not be enough. For basic use cases, however, you can omit that.
The example uses spring-boot-starter-web, because we produce and consume messages in the same application. Real world solutions, however, have these functionalities separated.
Producing JMS Messages
For the sake of simplicity, we’ll be exchanging simple text messages. The aforementioned official Spring tutorial covers sending structured messages and it also explains how JSON messages are getting converted to/from simple Java POJOs.
We’re however focusing on the details of integrating SQS as our message broker instead.
For producing messages a simple REST controller (MessageProducer) is used which in turn puts the HTTP request’s body to an SQS queue. It’s fairly trivial to do just that, after all, there’s nothing specific to SQS in that piece of code.
@RestController
@Slf4j
public class MessageProducer {
private final JmsOperations jmsTemplate;
@Autowired
public MessageProducer(JmsOperations jmsTemplate) {
this.jmsTemplate = jmsTemplate;
}
@PostMapping("/message")
public ResponseEntity sendMessage(@RequestBody String message) {
log.info("Sending message {}.", message);
jmsTemplate.convertAndSend(message);
return ResponseEntity.ok().build();
}
}
Receiving JMS Messages
Spring FrameworkSpring Boot
Opinions expressed by DZone contributors are their own.
Related
How to Implement Specific Distributed System Patterns Using Spring Boot: Introduction
Integrate Spring With Open AI
Distributed Task Synchronization: Leveraging ShedLock in Spring
Be Punctual! Avoiding Kotlin’s lateinit In Spring Boot Testing
Comments