-
Notifications
You must be signed in to change notification settings - Fork 99
Integrating Redis Streams into Redis OM Spring #610
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
foogaro
wants to merge
11
commits into
redis:main
Choose a base branch
from
foogaro:redis-stream-integration
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
b718ae4
Integrated Redis Cache and Redis Sessions into Redis OM Spring.
foogaro cea1af7
Integrated Redis Streams into Redis OM Spring.
foogaro f293a48
Added javadocs to the Redis Streams classes.
foogaro 2380079
Added the jakarta.annoation-api dependency.
foogaro 7d14e77
Fixed the creation of the GeoLoc when using a String.
foogaro 851d5b2
Refactored the RedisStreams integration by providing better enablemen…
foogaro 02962d4
Fixing the spotless.
foogaro 00769a5
Added comments to the RedisStreams Spring integration classes.
foogaro 57fa12c
Updated the HF urls by adding the HF domain.
foogaro 8fd98c2
Added the demo project for RedisStreams.
foogaro 5a17624
Reference to the redis-om-spring dependency declared as project.
foogaro File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,333 @@ | ||
# Redis Streams Consumer Framework | ||
|
||
This framework provides automatic bean creation for Redis Stream consumers using Spring annotations. | ||
|
||
## Overview | ||
|
||
The framework consists of two main annotations: | ||
- `@EnableRedisStreams`: Enables the automatic scanning and bean creation for Redis Stream consumers | ||
- `@RedisStreamConsumer`: Marks a class as a Redis Stream consumer with specific configuration | ||
|
||
## Quick Start | ||
|
||
### 1. Enable Redis Streams | ||
|
||
Add the `@EnableRedisStreams` annotation to your configuration class: | ||
|
||
```java | ||
@Configuration | ||
@EnableRedisStreams(basePackages = "com.redis.om.streams.consumer") | ||
public class RedisStreamsConfiguration { | ||
// Your configuration | ||
} | ||
``` | ||
|
||
### 2. Create a Consumer | ||
|
||
Create a class that extends `RedisStreamsConsumer` and annotate it with `@RedisStreamConsumer`: | ||
|
||
```java | ||
@RedisStreamConsumer( | ||
topicName = "myTopic", | ||
groupName = "myGroup", | ||
consumerName = "myConsumer", | ||
autoAck = false, | ||
cluster = false | ||
) | ||
public class MyRedisStreamsConsumer extends RedisStreamsConsumer { | ||
|
||
protected final Logger logger = LoggerFactory.getLogger(getClass()); | ||
|
||
@Scheduled(fixedDelayString = "${redis.streams.fixed-delay:1000}") | ||
public boolean process() { | ||
TopicEntry topicEntry = consume(); | ||
if (topicEntry != null) { | ||
logger.info("{} processing topic: {}", getClass().getSimpleName(), topicEntry); | ||
return true; | ||
} | ||
return false; | ||
} | ||
} | ||
``` | ||
|
||
## Annotation Configuration | ||
|
||
### @EnableRedisStreams | ||
|
||
| Attribute | Type | Default | Description | | ||
|-----------|------|---------|-------------| | ||
| `basePackages` | String[] | `{"com.redis.om.streams"}` | Base packages to scan for `@RedisStreamConsumer` annotated classes | | ||
| `value` | String[] | `{}` | Alias for `basePackages` | | ||
|
||
### @RedisStreamConsumer | ||
|
||
| Attribute | Type | Default | Description | | ||
|-----------|------|---------|----------------------------------------------------------------| | ||
| `topicName` | String | **Required** | Name of the Redis Stream topic | | ||
| `groupName` | String | **Required** | Name of the consumer group | | ||
| `consumerName` | String | `""` | Name of the consumer within the group | | ||
| `autoAck` | boolean | `false` | Whether the consumer can acknowledge messages | | ||
| `cluster` | boolean | `false` | Whether to use cluster mode | | ||
|
||
## Consumer Types | ||
|
||
The framework automatically creates different types of consumer groups based on the annotation configuration: | ||
|
||
### 1. No Acknowledgment Consumer (default) | ||
```java | ||
@RedisStreamConsumer( | ||
topicName = "topic", | ||
groupName = "group", | ||
autoAck = false, | ||
cluster = false | ||
) | ||
``` | ||
Creates: `NoAckConsumerGroup` | ||
|
||
### 2. Acknowledgment Consumer | ||
```java | ||
@RedisStreamConsumer( | ||
topicName = "topic", | ||
groupName = "group", | ||
autoAck = true, | ||
cluster = false | ||
) | ||
``` | ||
Creates: `ConsumerGroup` | ||
|
||
### 3. Cluster Consumer | ||
```java | ||
@RedisStreamConsumer( | ||
topicName = "topic", | ||
groupName = "group", | ||
autoAck = true, | ||
cluster = true | ||
) | ||
``` | ||
Creates: `SingleClusterPelConsumerGroup` | ||
|
||
## Automatic Bean Creation | ||
|
||
When you use `@EnableRedisStreams`, the framework automatically creates the following beans for each consumer: | ||
|
||
1. **SerialTopicConfig**: Configuration for the topic | ||
2. **TopicManager**: Manages the Redis Stream topic | ||
3. **ConsumerGroup**: The appropriate consumer group based on configuration | ||
4. **Consumer Class**: The annotated consumer class itself | ||
|
||
### Bean Naming Convention | ||
|
||
- `SerialTopicConfig`: `{topicName}SerialTopicConfig` | ||
- `TopicManager`: `{topicName}TopicManager` (unique per topic, shared between consumers of the same topic) | ||
- `ConsumerGroup`: `{groupName}ConsumerGroup` or `{groupName}NoAckConsumerGroup` or `{groupName}SingleClusterPelConsumerGroup` (unique per group, shared between consumers of the same group) | ||
- `Consumer Class`: `{className}` (uncapitalized) | ||
|
||
## Requirements | ||
|
||
### Method Requirements | ||
|
||
Every consumer class must have a `process()` method annotated with `@Scheduled`: | ||
|
||
```java | ||
@Scheduled(fixedDelayString = "${redis.streams.fixed-delay:1000}") | ||
public boolean process() { | ||
// Your processing logic here | ||
TopicEntry topicEntry = consume(); | ||
// Process the message | ||
return acknowledge(topicEntry); // or return true/false | ||
} | ||
``` | ||
|
||
### Dependencies | ||
|
||
Make sure you have the following dependencies in your `pom.xml`: | ||
|
||
```xml | ||
<dependency> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-starter</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-starter-web</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>com.redis.om</groupId> | ||
<artifactId>redis-om-spring</artifactId> | ||
<version>1.0.0-RC3</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.projectlombok</groupId> | ||
<artifactId>lombok</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>com.fasterxml.jackson.core</groupId> | ||
<artifactId>jackson-databind</artifactId> | ||
</dependency> | ||
``` | ||
|
||
### Configuration | ||
|
||
Ensure that `@EnableRedisStreams` is enabled in your configuration: | ||
|
||
```java | ||
@Configuration | ||
@EnableRedisStreams(basePackages = "com.redis.om.streams.consumer") | ||
public class RedisStreamsConfiguration { | ||
// Your configuration | ||
} | ||
``` | ||
|
||
## Examples | ||
|
||
### Example 1: Basic Consumer with Consumer Name | ||
```java | ||
@RedisStreamConsumer(topicName = "topicFoo", groupName = "groupFoo", consumerName = "Foo") | ||
public class FooRedisStreamsConsumer extends RedisStreamsConsumer { | ||
|
||
protected final Logger logger = LoggerFactory.getLogger(getClass()); | ||
|
||
@Scheduled(fixedDelayString = "${redis.streams.fixed-delay:1000}") | ||
public boolean process() { | ||
TopicEntry topicEntry = consume(); | ||
if (topicEntry != null) { | ||
logger.info("{} processing topic: {}", getClass().getSimpleName(), topicEntry); | ||
} | ||
return true; | ||
} | ||
} | ||
``` | ||
|
||
### Example 2: Acknowledgment Consumer | ||
```java | ||
@RedisStreamConsumer( | ||
topicName = "topicFoo", | ||
groupName = "groupFoo", | ||
autoAck = true | ||
) | ||
public class AckRedisStreamsConsumer extends RedisStreamsConsumer { | ||
|
||
protected final Logger logger = LoggerFactory.getLogger(getClass()); | ||
|
||
@Scheduled(fixedDelayString = "${redis.streams.fixed-delay:1000}") | ||
public boolean process() { | ||
TopicEntry topicEntry = consume(); | ||
if (topicEntry != null) { | ||
logger.info("{} processing topic: {}", getClass().getSimpleName(), topicEntry); | ||
return acknowledge(topicEntry); | ||
} | ||
return false; | ||
} | ||
} | ||
``` | ||
|
||
### Example 3: No-Ack Consumer (Explicit) | ||
```java | ||
@RedisStreamConsumer( | ||
topicName = "topicFoo", | ||
groupName = "groupFoo", | ||
autoAck = false | ||
) | ||
public class NoAckFooRedisStreamsConsumer extends RedisStreamsConsumer { | ||
|
||
protected final Logger logger = LoggerFactory.getLogger(getClass()); | ||
|
||
@Scheduled(fixedDelayString = "${redis.streams.fixed-delay:1000}") | ||
public boolean process() { | ||
TopicEntry topicEntry = consume(); | ||
if (topicEntry != null) { | ||
logger.info("{} processing topic: {}", getClass().getSimpleName(), topicEntry); | ||
} | ||
return true; | ||
} | ||
} | ||
``` | ||
|
||
## Configuration Properties | ||
|
||
Configure your application in `application.properties`: | ||
|
||
```properties | ||
# Server Configuration | ||
server.port=8080 | ||
spring.application.name=redis-om-spring-streams | ||
|
||
# Spring Data Redis Configuration | ||
spring.data.redis.host=localhost | ||
spring.data.redis.port=6379 | ||
spring.data.redis.username= | ||
spring.data.redis.password= | ||
|
||
# Redis Streams Configuration | ||
redis.streams.fixed-delay=5000 | ||
``` | ||
|
||
## Logging | ||
|
||
The framework provides detailed logging for bean creation and consumer operations. You can configure logging levels in your `application.properties`: | ||
|
||
```properties | ||
logging.level.com.redis.om.streams.config=INFO | ||
logging.level.com.redis.om.streams.consumer=INFO | ||
``` | ||
|
||
## Error Handling | ||
|
||
The framework handles various error scenarios: | ||
|
||
- **ClassNotFoundException**: Logs error and continues with other consumers | ||
- **InvalidTopicException**: Throws IllegalStateException during TopicManager creation | ||
- **TopicNotFoundException**: Handled by individual consumers | ||
- **InvalidMessageException**: Handled by producers | ||
- **ProducerTimeoutException**: Handled by producers | ||
|
||
## Best Practices | ||
|
||
1. **Package Organization**: Keep your consumers in dedicated packages for better organization | ||
2. **Bean Naming**: Use descriptive topic and group names to avoid conflicts | ||
3. **Error Handling**: Implement proper error handling in your `process()` methods | ||
4. **Logging**: Use appropriate log levels for debugging and monitoring | ||
5. **Configuration**: Use environment-specific configurations for different deployment environments | ||
6. **Scheduling**: Use configurable delays with `fixedDelayString` for easy tuning | ||
|
||
## Troubleshooting | ||
|
||
### Common Issues | ||
|
||
1. **No beans created**: Check that `@EnableRedisStreams` is properly configured and base packages are correct | ||
2. **Scheduling not working**: Ensure `@EnableScheduling` is enabled in your configuration | ||
3. **Redis connection issues**: Verify Redis connection configuration in `application.properties` | ||
4. **Bean conflicts**: Check for duplicate bean names, especially with topic configurations | ||
5. **Message production issues**: Verify that the `Producer` bean is properly configured | ||
|
||
### Debug Mode | ||
|
||
Enable debug logging to see detailed bean creation information: | ||
|
||
```properties | ||
logging.level.com.redis.om.streams=DEBUG | ||
``` | ||
|
||
## Sample project structure for this demo | ||
|
||
``` | ||
src/main/java/com/redis/om/streams/ | ||
├── config/ | ||
│ └── RedisStreamsConfiguration.java | ||
├── consumer/ | ||
│ ├── AckRedisStreamsConsumer.java | ||
│ ├── FooRedisStreamsConsumer.java | ||
│ └── NoAckFooRedisStreamsConsumer.java | ||
├── controller/ | ||
│ └── StreamsController.java | ||
├── model/ | ||
│ └── TextData.java | ||
└── DemoApplication.java | ||
``` | ||
|
||
## Running the Application | ||
|
||
1. Start Redis server | ||
2. Run the Spring Boot application | ||
3. Use the REST endpoints to produce messages | ||
4. Watch the consumer logs to see message processing |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@foogaro Can we get some producer examples in here as well?