-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: enabled basic usage of RedisClusterClient
- Loading branch information
1 parent
27873c0
commit ce380e4
Showing
11 changed files
with
541 additions
and
281 deletions.
There are no files selected for viewing
This file contains 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
250 changes: 250 additions & 0 deletions
250
connector-java/src/main/java/io/zeebe/redis/connect/java/RedisConnectionBuilder.java
This file contains 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,250 @@ | ||
package io.zeebe.redis.connect.java; | ||
|
||
import io.camunda.zeebe.protocol.record.ValueType; | ||
import io.lettuce.core.RedisBusyException; | ||
import io.lettuce.core.RedisClient; | ||
import io.lettuce.core.XGroupCreateArgs; | ||
import io.lettuce.core.XReadArgs; | ||
import io.lettuce.core.cluster.RedisClusterClient; | ||
import io.zeebe.exporter.proto.Schema; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import java.time.Duration; | ||
import java.util.*; | ||
import java.util.function.Consumer; | ||
|
||
public class RedisConnectionBuilder { | ||
|
||
private static final Logger LOGGER = LoggerFactory.getLogger(RedisConnectionBuilder.class); | ||
|
||
private static final int XREAD_BLOCK_MILLISECONDS = 2000; | ||
private static final int XREAD_COUNT = 500; | ||
private final UniversalRedisClient redisClient; | ||
|
||
private boolean reconnectUsesNewConnection = false; | ||
private Duration reconnectInterval = Duration.ofSeconds(1); | ||
|
||
private final Map<String, List<Consumer<?>>> listeners = new HashMap<>(); | ||
|
||
private String consumerGroup = UUID.randomUUID().toString(); | ||
|
||
private boolean shouldDestroyConsumerGroupOnClose = true; | ||
|
||
private String consumerId = UUID.randomUUID().toString(); | ||
private String prefix = "zeebe:"; | ||
|
||
private String offset = "0-0"; | ||
|
||
private int xreadBlockMillis = XREAD_BLOCK_MILLISECONDS; | ||
|
||
private int xreadCount = XREAD_COUNT; | ||
|
||
private boolean deleteMessages = false; | ||
|
||
RedisConnectionBuilder(RedisClient redisClient) { | ||
this.redisClient = new UniversalRedisClient(redisClient); | ||
} | ||
|
||
RedisConnectionBuilder(RedisClusterClient redisClient) { | ||
this.redisClient = new UniversalRedisClient(redisClient); | ||
} | ||
|
||
public RedisConnectionBuilder withReconnectUsingNewConnection() { | ||
this.reconnectUsesNewConnection = true; | ||
return this; | ||
} | ||
|
||
public RedisConnectionBuilder reconnectInterval(Duration duration) { | ||
this.reconnectInterval = duration; | ||
return this; | ||
} | ||
|
||
/** | ||
* Sets the XREAD [BLOCK milliseconds] parameter. Default is 2000. | ||
*/ | ||
public RedisConnectionBuilder xreadBlockMillis(int xreadBlockMillis) { | ||
this.xreadBlockMillis = xreadBlockMillis; | ||
return this; | ||
} | ||
|
||
/** | ||
* Sets the XREAD [COUNT count] parameter. Default is 1000. | ||
*/ | ||
public RedisConnectionBuilder xreadCount(int xreadCount) { | ||
this.xreadCount = xreadCount; | ||
return this; | ||
} | ||
|
||
/** | ||
* Set the consumer group, e.g. the application name. | ||
*/ | ||
public RedisConnectionBuilder consumerGroup(String consumerGroup) { | ||
this.consumerGroup = consumerGroup; | ||
this.shouldDestroyConsumerGroupOnClose = false; | ||
return this; | ||
} | ||
|
||
/** | ||
* Set the unique consumer ID. | ||
*/ | ||
public RedisConnectionBuilder consumerId(String consumerId) { | ||
this.consumerId = consumerId; | ||
return this; | ||
} | ||
|
||
/** | ||
* Set the prefix for the Streams to read from. | ||
*/ | ||
public RedisConnectionBuilder prefix(String name) { | ||
this.prefix = name + ":"; | ||
return this; | ||
} | ||
|
||
/** | ||
* Start reading from a given offset. | ||
*/ | ||
public RedisConnectionBuilder offset(String offset) { | ||
this.offset = offset; | ||
return this; | ||
} | ||
|
||
public RedisConnectionBuilder deleteMessagesAfterSuccessfulHandling(boolean deleteMessages) { | ||
this.deleteMessages = deleteMessages; | ||
return this; | ||
} | ||
|
||
private <T extends com.google.protobuf.Message> void addListener( | ||
String valueType, Consumer<T> listener) { | ||
final var recordListeners = listeners.getOrDefault(valueType, new ArrayList<>()); | ||
recordListeners.add(listener); | ||
listeners.put(prefix + valueType, recordListeners); | ||
} | ||
|
||
public RedisConnectionBuilder addDeploymentListener(Consumer<Schema.DeploymentRecord> listener) { | ||
addListener(ValueType.DEPLOYMENT.name(), listener); | ||
return this; | ||
} | ||
|
||
public RedisConnectionBuilder addDeploymentDistributionListener( | ||
Consumer<Schema.DeploymentDistributionRecord> listener) { | ||
addListener(ValueType.DEPLOYMENT_DISTRIBUTION.name(), listener); | ||
return this; | ||
} | ||
|
||
public RedisConnectionBuilder addProcessListener(Consumer<Schema.ProcessRecord> listener) { | ||
addListener(ValueType.PROCESS.name(), listener); | ||
return this; | ||
} | ||
|
||
public RedisConnectionBuilder addProcessInstanceListener(Consumer<Schema.ProcessInstanceRecord> listener) { | ||
addListener(ValueType.PROCESS_INSTANCE.name(), listener); | ||
return this; | ||
} | ||
|
||
public RedisConnectionBuilder addProcessEventListener(Consumer<Schema.ProcessEventRecord> listener) { | ||
addListener(ValueType.PROCESS_EVENT.name(), listener); | ||
return this; | ||
} | ||
|
||
public RedisConnectionBuilder addVariableListener(Consumer<Schema.VariableRecord> listener) { | ||
addListener(ValueType.VARIABLE.name(), listener); | ||
return this; | ||
} | ||
|
||
public RedisConnectionBuilder addVariableDocumentListener(Consumer<Schema.VariableDocumentRecord> listener) { | ||
addListener(ValueType.VARIABLE_DOCUMENT.name(), listener); | ||
return this; | ||
} | ||
|
||
public RedisConnectionBuilder addJobListener(Consumer<Schema.JobRecord> listener) { | ||
addListener(ValueType.JOB.name(), listener); | ||
return this; | ||
} | ||
|
||
public RedisConnectionBuilder addJobBatchListener(Consumer<Schema.JobBatchRecord> listener) { | ||
addListener(ValueType.JOB_BATCH.name(), listener); | ||
return this; | ||
} | ||
|
||
public RedisConnectionBuilder addIncidentListener(Consumer<Schema.IncidentRecord> listener) { | ||
addListener(ValueType.INCIDENT.name(), listener); | ||
return this; | ||
} | ||
|
||
public RedisConnectionBuilder addTimerListener(Consumer<Schema.TimerRecord> listener) { | ||
addListener(ValueType.TIMER.name(), listener); | ||
return this; | ||
} | ||
|
||
public RedisConnectionBuilder addMessageListener(Consumer<Schema.MessageRecord> listener) { | ||
addListener(ValueType.MESSAGE.name(), listener); | ||
return this; | ||
} | ||
|
||
public RedisConnectionBuilder addMessageSubscriptionListener( | ||
Consumer<Schema.MessageSubscriptionRecord> listener) { | ||
addListener(ValueType.MESSAGE_SUBSCRIPTION.name(), listener); | ||
return this; | ||
} | ||
|
||
public RedisConnectionBuilder addMessageStartEventSubscriptionListener( | ||
Consumer<Schema.MessageStartEventSubscriptionRecord> listener) { | ||
addListener(ValueType.MESSAGE_START_EVENT_SUBSCRIPTION.name(), listener); | ||
return this; | ||
} | ||
|
||
public RedisConnectionBuilder addProcessMessageSubscriptionListener( | ||
Consumer<Schema.ProcessMessageSubscriptionRecord> listener) { | ||
addListener(ValueType.PROCESS_MESSAGE_SUBSCRIPTION.name(), listener); | ||
return this; | ||
} | ||
|
||
public RedisConnectionBuilder addProcessInstanceCreationListener( | ||
Consumer<Schema.ProcessInstanceCreationRecord> listener) { | ||
addListener(ValueType.PROCESS_INSTANCE_CREATION.name(), listener); | ||
return this; | ||
} | ||
|
||
public RedisConnectionBuilder addErrorListener(Consumer<Schema.ErrorRecord> listener) { | ||
addListener(ValueType.ERROR.name(), listener); | ||
return this; | ||
} | ||
|
||
/** | ||
* Start a background task that reads from Zeebe Streams. | ||
* <br> | ||
* Call {@link #close()} to stop reading. | ||
*/ | ||
public ZeebeRedis build() { | ||
if (listeners.size() == 0) { | ||
throw new IllegalArgumentException("Must register a least one listener, but none found."); | ||
} | ||
|
||
|
||
final var connection = redisClient.connect(new ProtobufCodec()); | ||
|
||
LOGGER.info("Read from Redis streams '{}*' with offset '{}'", prefix, offset); | ||
|
||
// Prepare | ||
var syncStreamCommands = connection.syncStreamCommands(); | ||
List<XReadArgs.StreamOffset<String>> offsets = new ArrayList<>(); | ||
listeners.keySet().stream().forEach(stream -> { | ||
offsets.add(XReadArgs.StreamOffset.lastConsumed(stream)); | ||
try { | ||
syncStreamCommands.xgroupCreate(XReadArgs.StreamOffset.from(stream, offset), consumerGroup, | ||
XGroupCreateArgs.Builder.mkstream()); | ||
} catch (RedisBusyException ex) { | ||
// NOOP: consumer group already exists | ||
} | ||
}); | ||
|
||
final var zeebeRedis = new ZeebeRedis(redisClient, connection, reconnectUsesNewConnection, reconnectInterval, | ||
xreadBlockMillis, xreadCount, consumerGroup, consumerId, prefix, | ||
offsets.toArray(new XReadArgs.StreamOffset[0]), listeners, deleteMessages, | ||
shouldDestroyConsumerGroupOnClose); | ||
zeebeRedis.start(); | ||
|
||
return zeebeRedis; | ||
} | ||
} |
24 changes: 24 additions & 0 deletions
24
connector-java/src/main/java/io/zeebe/redis/connect/java/UniversalRedisClient.java
This file contains 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,24 @@ | ||
package io.zeebe.redis.connect.java; | ||
|
||
import io.lettuce.core.RedisClient; | ||
import io.lettuce.core.cluster.RedisClusterClient; | ||
|
||
public class UniversalRedisClient { | ||
|
||
private RedisClient redisClient = null; | ||
private RedisClusterClient redisClusterClient = null; | ||
|
||
public UniversalRedisClient(RedisClient redisClient) { | ||
this.redisClient = redisClient; | ||
} | ||
|
||
public UniversalRedisClient(RedisClusterClient redisClient) { | ||
this.redisClusterClient = redisClient; | ||
} | ||
|
||
public UniversalRedisConnection<String, byte[]> connect(ProtobufCodec protobufCodec) { | ||
if (redisClient != null) return new UniversalRedisConnection<>(redisClient.connect(protobufCodec)); | ||
return new UniversalRedisConnection<>(redisClusterClient.connect(protobufCodec)); | ||
} | ||
|
||
} |
43 changes: 43 additions & 0 deletions
43
connector-java/src/main/java/io/zeebe/redis/connect/java/UniversalRedisConnection.java
This file contains 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,43 @@ | ||
package io.zeebe.redis.connect.java; | ||
|
||
import io.lettuce.core.RedisConnectionStateListener; | ||
import io.lettuce.core.api.StatefulConnection; | ||
import io.lettuce.core.api.StatefulRedisConnection; | ||
import io.lettuce.core.api.async.RedisStreamAsyncCommands; | ||
import io.lettuce.core.api.sync.RedisStreamCommands; | ||
import io.lettuce.core.cluster.api.StatefulRedisClusterConnection; | ||
|
||
public class UniversalRedisConnection<K, V> { | ||
|
||
private StatefulRedisConnection<K, V> redisConnection = null; | ||
|
||
private StatefulRedisClusterConnection<K, V> redisClusterConnection = null; | ||
|
||
private StatefulConnection theConnection; | ||
public UniversalRedisConnection(StatefulConnection<K,V> redisConnection) { | ||
this.theConnection = redisConnection; | ||
if (redisConnection instanceof StatefulRedisConnection) { | ||
this.redisConnection = (StatefulRedisConnection<K, V>) redisConnection; | ||
} else { | ||
this.redisClusterConnection = (StatefulRedisClusterConnection<K, V>) redisConnection; | ||
} | ||
} | ||
|
||
public void addListener(RedisConnectionStateListener listener) { | ||
theConnection.addListener(listener); | ||
} | ||
|
||
public RedisStreamCommands<K, V> syncStreamCommands() { | ||
if (redisConnection != null) return redisConnection.sync(); | ||
return redisClusterConnection.sync(); | ||
} | ||
|
||
public RedisStreamAsyncCommands<K, V> asyncStreamCommands() { | ||
if (redisConnection != null) return redisConnection.async(); | ||
return redisClusterConnection.async(); | ||
} | ||
|
||
public void close() { | ||
theConnection.close(); | ||
} | ||
} |
Oops, something went wrong.