All about Reliable Queue for Valkey and Redis
As the requirements for enterprise-grade applications became more stringent, developers abandoned the old, monolithic approach to programming. They found that when too many components of modern apps communicate synchronously through API calls, there is a tight coupling between services, which often results in performance bottlenecks and reliability issues. If one service experiences an unexpected failure or a sudden surge in traffic, it can trigger a failure that cascades through the entire application.
So, monolithic design was out, and the microservice architecture was in. Developers soon found that message queues are an elegant, practical solution in the new application design paradigm. Message queues fundamentally decouple message producers from their consumers, allowing for asynchronous data processing.
Many tools and services can serve as a message queue, but one of the most popular is the speedy in-memory data store Valkey, an open-source fork of Redis. But as good as the standard Valkey/Redis queue is, Java developers have an even better option: Reliable Queue.
Only available with Redisson PRO, the Java client for Valkey or Redis, Reliable Queue offers a new level of durability unmatched by other message queues. If you or someone on your team is a Java developer, here’s what you need to know about Redisson PRO’s Reliable Queue for Valkey and Redis.
Bridging Java and Valkey/Redis for Easier Messaging
Before diving into Reliable Queue itself, it helps to understand exactly what Redisson PRO is and how it simplifies working with message queues for Java developers.
As the leading Java client for Valkey and Redis, Redisson PRO provides a comprehensive suite of distributed objects and services. With Redisson PRO, Java developers can leverage familiar Java interfaces, such as java.util.Queue to easily work with distributed data structures.
Redisson PRO’s Reliable Queue: The Key Features
Reliable Queue enhances standard Valkey/Redis queues with a wide range of enterprise-level features, including:
Exactly-Once Delivery & Acknowledgments
Reliable Queue guarantees exactly-once delivery by ensuring messages are only removed from the queue upon successful acknowledgment by the consumer. It supports both manual and automatic message acknowledgment. Many other queue implementations have no support for acknowledgements, leaving it up to developers to create their own error-checking and handling routines.
Negative Acknowledgements (NACK)
Most applications and services used for message queues lack support for negative acknowledgements. Also known as NACK, this is an acknowledgement of failed message delivery. Application logic can be applied to messages with a NACK status, such as retrying delivery or sending the message to a dead-letter queue (DLQ). Between Reliable Queue's exactly-once delivery and NACKs, developers can have complete confidence that messages are successfully delivered only once, and they can be handled appropriately if not.
Message Priority
Unlike many standard queue implementations, Reliable Queue allows developers to set message priority levels, from 0 to 9. This ensures critical messages are processed ahead of less important ones. Without the ability to set priority levels, all messages are processed in a typical first-in, first-out (FIFO) fashion, with no extra logic.
Configurable Size Limits for Messages and Queues
Most queue implementations impose a standard message size (often 256 KB) but typically make queue sizes unlimited. Reliable Queue offers developers more fine-grained control with configurable limits for both messages and queue sizes.
Message Visibility Timeout
A configurable visibility timeout, applicable per message or queue, temporarily makes a message invisible to other consumers once it’s picked up, preventing multiple processing attempts. Queues without this feature run the risk of duplicate processing.
Deduplication
Reliable Queue prevents message duplication by comparing message IDs or payload hashes within a configurable timeframe. When combined with Redisson PRO's other queue management features, developers can be sure messages are delivered and processed only once by the intended consumer.
Message Headers (Metadata)
Redisson PRO supports rich metadata stored in message headers. In applications built on the microservices architecture, this extra bit of contextual information enables advanced message routing, filtering, and more.
Fine-Grained Control With a Universal API
While Redisson PRO and Reliable Queue offer even more features than what’s listed above, it’s important to understand all this functionality in context.
Ultimately, developers don’t automatically prefer a solution just because it has “more features.” What they really want is the “right set of features.” Reliable Queue meets this requirement with fine-grained control over the entire message and error handling lifecycle. Reliable Queue achieves this with its ability to prioritize urgent messages, define precise retry logic with NACKs, or schedule tasks with exact timing. This fine-grained level of control allows developers to implement specific business logic directly within the messaging layer.
In addition, it’s easy for Java developers to integrate Reliable Queue, even into existing Java enterprise applications. That’s because Redisson PRO has a universal API model that supports synchronous, asynchronous, reactive, and RxJava3 interfaces. It integrates effortlessly with popular Java enterprise frameworks and ecosystems, including Spring Boot, Spring Cloud Stream (offering a dedicated binder for Redis/Valkey), Quarkus, Micronaut, and Hibernate.
Redisson PRO is therefore a good fit for all modern Java development practices and application architectures. Take a look at these code samples to see Reliable Queue and other Redisson PRO queue features in action, with comments explaining each function:
public void addSimpleMessage(RedissonClient redisson) {
RReliableQueue queue = redisson.getReliableQueue("simple-queue");
// Add a simple message
Message msg = queue.add(QueueAddArgs.messages(
MessageArgs.payload("Hello, Reliable Queue!")
));
System.out.println("Message ID: " + msg.getId());
System.out.println("Payload: " + msg.getPayload());
}
public void addMessageWithSettings(RedissonClient redisson) {
RReliableQueue queue = redisson.getReliableQueue("order-queue");
Order order = new Order("ORD-123", 99.99, "John Doe");
// Add message with custom settings
Message msg = queue.add(QueueAddArgs.messages(
MessageArgs.payload(order)
.deliveryLimit(10)
.timeToLive(Duration.ofDays(7))
.header("orderType", "PREMIUM")
.header("region", "US-EAST")
.delay(Duration.ofMinutes(5))
.priority(7) // High priority
.deduplicationById("ORD-123", Duration.ofHours(1))
));
System.out.println("Order message added: " + msg.getId());
printMessageDetails(msg);
}
public void addToQueueWithSizeLimit(RedissonClient redisson) {
RReliableQueue queue = redisson.getReliableQueue("limited-queue");
// Configure queue with size limit
queue.setConfig(QueueConfig.defaults().maxSize(100));
// Add message with timeout (blocks if queue is full)
Message msg = queue.add(QueueAddArgs
.messages(MessageArgs.payload("Important data"))
.timeout(Duration.ofSeconds(30))
);
if (msg != null) {
System.out.println("Message added successfully: " + msg.getId());
} else {
System.out.println("Failed to add message - timeout expired");
}
}
public void pollSingleMessage(RedissonClient redisson) {
RReliableQueue queue = redisson.getReliableQueue("order-queue");
// Poll a single message with manual acknowledgment
Message msg = queue.poll(QueuePollArgs.defaults()
.visibility(Duration.ofSeconds(30))
.acknowledgeMode(AcknowledgeMode.MANUAL)
);
if (msg != null) {
System.out.println("Received message: " + msg.getId());
System.out.println("Order: " + msg.getPayload());
// Process the message
boolean processed = processOrder(msg.getPayload());
if (processed) {
// Acknowledge successful processing
queue.acknowledge(QueueAckArgs.ids(msg.getId()));
System.out.println("Message acknowledged");
} else {
// Negative acknowledgment for failed processing
queue.negativeAcknowledge(QueueNegativeAckArgs
.failed(msg.getId())
.delay(Duration.ofSeconds(10))
);
System.out.println("Message processing failed, will retry");
}
} else {
System.out.println("No messages available");
}
}
public void pollBatchMessages(RedissonClient redisson) {
RReliableQueue queue = redisson.getReliableQueue("order-queue");
// Poll multiple messages with auto acknowledgment
List> messages = queue.pollMany(QueuePollArgs.defaults()
.acknowledgeMode(AcknowledgeMode.AUTO)
.count(10) // Get up to 10 messages
.visibility(Duration.ofMinutes(2))
);
System.out.println("Received " + messages.size() + " messages");
for (Message msg : messages) {
System.out.println("Processing auto-ack message: " + msg.getId());
processOrder(msg.getPayload());
// No need to acknowledge - auto mode
}
}
Is Reliable Queue Right For You?
Now that you’ve learned about Reliable Queue and seen how Redisson PRO brings advanced message handling to Valkey/Redis, the value proposition should be clear. Reliable Queue is enterprise-grade, feature-rich, and highly configurable. But is it right for you or your development team?
Here are some scenarios where Redisson PRO and Reliable Queue will be a good fit if:
- Your development environment is Java-centric, and you need fine-grained control over message flow and error handling.
- You already use or plan to deploy Valkey/Redis for caching, session management, real-time analytics, or other data storage needs.
- Your applications require advanced features beyond standard queue functionality, such as setting message priorities or creating retry logic based on NACKs.
- Your applications are highly latency-sensitive or depend on consistent, high-throughput performance.
- Your existing message queue service or tool lacks the features or durability your applications require.
For scenarios requiring message broadcasting to multiple consumers, Reliable Queue also supports a fanout pattern that publishes each message to multiple queues simultaneously while maintaining all reliability guarantees including acknowledgments, deduplication, and exactly-once delivery per queue.