Errors happen, and Spring Cloud Stream provides several flexible mechanisms to handle them.
Here is a visualization of the “happy path” for a publish-subscribe operation when using a Spring Cloud Stream with the Solace binder to create your event-driven microservices.
Terminology
Before we dive into the error handling functionalities and strategies, let us quickly review the key terminologies related to Error Handling in Spring Cloud Stream binder for Solace PubSub+.
- Dead Message Queue (DMQ): A designated queue on the message broker that captures re-routed failed messages due to TTL expiration or exceeding the message’s maximum redelivery count.
- Error Queue: Error queue is a Queue Endpoint on the broker that is used to capture re-routed messages which have been successfully consumed from the message broker yet cannot be processed by the Spring Cloud Stream application.
- Error Handlers: Custom error handlers at a binding-level or a default error handler to receive and process the error messages.
- Error Queue Auto-binding :A consumer config property, autoBindErrorQueue, that specifies whether to automatically create a durable error queue to republish messages when message processing failures are encountered.
- Error Queue Custom Names: Override the opinionated queue naming scheme of Spring Cloud Stream binder for Solace PubSub+ and use custom names for error queues using For more information, check out the blog post Custom Name for your Queues when using Spring Cloud Stream Binder for Solace PubSub+.
- Manual Acknowledgment: Manually acknowledging a received message with the status ACCEPT, REJECT, or REQUEUE. For more information, check out the blog post Confirming Message Publication and Acknowledging Receipt with Spring Cloud Stream Binder for Solace PubSub+.
- Message Acknowledgement: The process of acknowledging the receipt of the message by the consumer. Options of automatic and manual acknowledgment are available. For more information, check out the blog post Confirming Message Publication and Acknowledging Receipt with Spring Cloud Stream Binder for Solace PubSub+.
- Message Handlers: A message handler is a component responsible for receiving, processing, and acting upon messages that are sent to a destination, typically a message channel or a topic.
- Publish Confirmation: The process of acknowledging the receipt of the message by the consumer. Options of automatic and manual acknowledgment are available. For more information, check out the blog post Confirming Message Publication and Acknowledging Receipt with Spring Cloud Stream Binder for Solace PubSub+.
- Message Redelivery: An error handling strategy where failed messages are redelivered to the consumer group’s queue.
Publish Operation
When publishing a message from your Spring Cloud Stream microservice, you simply send a message to the broker on a topic. While this sounds simple, several different error scenarios can occur. Here are a few possible failures:
- ACL permission issues
- A consumer’s queue is full
If one of these failures were to occur, the Solace binder would throw an exception wrapping details on the failure. As a developer, you can catch these exceptions, take appropriate action, or log the failures for review.
Spring Cloud Stream binder for Solace PubSub+ provides a facility to get confirmation on a publish operation via the Publish Confirmation feature. It is implemented by setting CorrelationData on the message header and getting a publish confirmation within the stipulated time. For more information, check out the blog post Confirming Message Publication and Acknowledging Receipt with Spring Cloud Stream Binder for Solace PubSub+.
Consume Operation
As you see in the diagram, when consuming a message using a Spring Cloud Stream microservice, a message from the Solace PubSub+ Broker passes through the Spring Cloud Stream framework and the binder before reaching the application code. The final entry point in your application is the message handler. An error can occur in any of these levels, and each layer provides error handling features. The binder implementation offers error handling facilities that take advantage of the Broker facilities such as DMQ and Message Redelivery.
What can go wrong, and how do we handle them? Let us review some of the error scenarios and how to handle them.
Your Spring Microservice application code
It’s all about the message processing in the handler – a runtime error can occur, or an application exception may be thrown in the code. Your Spring Microservice could have multiple bindings and require different logic to handle errors at a binding level. Also, you may need a generic error handler to handle errors occurred in bindings that do not have specific error handlers.
Spring Cloud Stream framework allows for defining error handlers at a binding level and a default error handler in the application configuration. If an error occurs in the message handler at runtime, the framework wraps the offending message as a Spring ErrorMessage and calls the registered error handlers.
In example 1, bindingSpecificErrorHandler has been registered as the error handler for binding functionOne. When an error occurs or an exception is thrown in the message handler, it is handed to the registered binding-specific error handler as an ErrorMessage, wrapping the original message.
In example 2, a default error handler has been registered with no binding-specific error handlers. In this scenario, when an error occurs or an exception is thrown in the message handler and no binding-specific error handler is present, it is handed to the default error handler as an ErrorMessage.
Spring Cloud Stream Framework
Outside of wiring up, your custom error handlers, as discussed in the previous section, a particular abstraction that is of interest with respect to error handling is the Spring Cloud Stream framework’s use of RetryTemplate provided by the Spring Retry library. This allows the framework to make several attempts at re-trying the same message with no additional code.
You can configure the retry template settings in the application configuration as suited to your message processing requirements.
Here is an example of retry template settings at a binding level.
... bindings: functionOne-in-0: error-handler-definition: bindingSpecificErrorHandler destination: solace/function/one group: errorhandler consumer: max-attempts: 3 binder: solace-broker ...
The Big Picture
The following diagram comprehensively captures the message flow and error handling when using Spring Cloud Stream binder for Solace PubSub+ to build microservices.
Common Error Scenarios
Message acknowledged with REJECT status. | Is an error queue is configured?
|
Message acknowledged with REQUEUE status | The message is simply negatively acknowledged and redelivered. |
Message conversion failed |
|
Max Retry Attempts exceeded | Has Spring Retry max attempt exceeded?
|
Exception thrown in message handler |
|
Tip: Don’t forget to set the errorMsgDmqEligible
setting in the application configuration if you want the discarded messages to be captured in the DMQ.
General Guidance
We have all these options available for use. How do we choose what to do when handling errors? Of course, it all goes back to your requirements.
In general, keep it simple when possible!
- Handle your exceptions, and don’t throw them when possible.
- Consider how your function might fail and configure the
RetryTemplate
(Would Retrying help?) and binding-specific error channels appropriately. - Do you want messages that throw exceptions to end up in another queue? (Use
autoBindErrorQueue
and name the queues reflecting the error context usingerrorQueueNameExpression
)
If you need more control and are fine writing more messaging-specific code, consider using both the internal framework and binder error-handling options in conjunction with Client/Manual Acknowledgements.
- Use the Client/Manual Ack to REQUEUE messages that end in an error scenario that may be successful if retried, even if by another instance in the Consumer Group. For example, an infrastructure issue where your microservice couldn’t get a response from a downstream service.
- Identify your failure scenarios that wouldn’t work if retried and consider if you want to send them all to one queue for further processing or to several destinations. If it is a single queue for all error messages, use the
autoBindErrorQueue
option, and use the Client/Manual Ack to REJECT the message and let the binder handle the queueing. - However, if you prefer to send to several destinations for error processing, use Dynamic Publishing to publish messages to destinations where you’d like. After publishing, use the Client/Manual Ack to ACCEPT the message.
Summary
As a developer, it is essential to understand the error-handling features supported by the Spring Cloud Stream framework and the Spring Cloud Stream binder for Solace PubSub+ to handle error scenarios gracefully in a programmatic manner. I hope this blog helped you to gain knowledge and set the course in that direction.
If you’re new to Spring Cloud Stream and want more help getting up and running, these are great places to learn more.
- Spring Cloud Stream binder for Solace PubSub+
- Getting Started with Spring Cloud Stream using Spring Initializr
- Getting Started with Spring Cloud Stream using start.spring.io and a PubSub+ Event Broker
- Samples for Spring Cloud Stream binder for Solace PubSub+
- Tutorials & Codelabs for Spring Cloud Stream binder for Solace PubSub+
And finally, Solace Community, where developers and experts exchange ideas, share thoughts, and discuss topics related to event-driven architecture.
Explore other posts from categories: For Architects | For Developers