#1
Redis Pub/Sub is useful for simple messaging, but it does not store messages. If a service is offline, messages can be lost. Redis Streams solve this problem by providing persistent messaging with consumer groups, making it suitable for event-driven microservices.
This article explains how to build a simple event-driven architecture using Redis Streams in Spring Boot.

1. What Are Redis Streams

Redis Streams are a data structure designed for append-only event logs.
Key features:
  • Persistent messages
  • Ordered event streams
  • Consumer groups for scalable processing
  • Message acknowledgments
Example stream structure:
orders-stream
 ├── message-1
 ├── message-2
 └── message-3
Each message gets a unique ID generated by Redis.

2. Creating a Stream Producer

A producer writes events to a Redis stream.
Example service:
@Service
public class OrderEventProducer {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    public void publishOrderEvent(String orderId, String status) {

        Map<String, String> event = new HashMap<>();
        event.put("orderId", orderId);
        event.put("status", status);

        redisTemplate.opsForStream()
                .add("orders-stream", event);
    }
}
Example event data:
orderId: 123
status: CREATED
This event is stored permanently in the stream.

3. Creating a Consumer Group

Consumer groups allow multiple services to process events without duplication.
Create a group:
redisTemplate.opsForStream()
        .createGroup("orders-stream", "order-processors");
Now multiple consumers can share the workload.

4. Implementing a Stream Consumer

Consumers read messages from the stream.
@Service
public class OrderEventConsumer {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    public void consumeEvents() {

        List<MapRecord<String, Object, Object>> messages =
                redisTemplate.opsForStream().read(
                        Consumer.from("order-processors", "consumer-1"),
                        StreamReadOptions.empty().count(10),
                        StreamOffset.create("orders-stream", ReadOffset.lastConsumed())
                );

        if(messages != null) {
            for (MapRecord<String, Object, Object> message : messages) {
                System.out.println("Processing event: " + message.getValue());

                redisTemplate.opsForStream()
                        .acknowledge("orders-stream",
                                "order-processors",
                                message.getId());
            }
        }
    }
}
After processing, the consumer sends an acknowledgment.

5. Running Consumers Periodically

Use a scheduled job to continuously read events.
@Scheduled(fixedDelay = 2000)
public void pollStream() {
    orderEventConsumer.consumeEvents();
}
This checks for new messages every two seconds.

6. Using Multiple Consumers

Consumer groups allow horizontal scaling.
Example:
orders-stream
   ↓
order-processors
   ├── consumer-1
   ├── consumer-2
   └── consumer-3
Each consumer processes different events, improving throughput.

7. Handling Failed Messages

If a consumer crashes before acknowledging a message, Redis keeps the event in the pending entries list (PEL).
Another consumer can reclaim and process it later.
This ensures reliable event processing.

8. Real Use Cases

Redis Streams are ideal for:
  • Microservice communication
  • Order processing systems
  • Event sourcing
  • Activity logging
  • Real-time analytics pipelines
#ads

image quote pre code