Skip to content
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

Added support for Redis Sentinel #375

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions dev-resources/redis-sentinel/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
version: '3.9'

services:
master:
image: "bitnami/redis:7.2.1"
environment:
- REDIS_DISABLE_COMMANDS=FLUSHDB,FLUSHALL,CONFIG
- REDIS_AOF_ENABLED=no
- ALLOW_EMPTY_PASSWORD=no
- REDIS_PASSWORD=passwd
- REDIS_REPLICATION_MODE=master
network_mode: host

replica:
image: "bitnami/redis:7.2.1"
environment:
- REDIS_REPLICATION_MODE=slave
- REDIS_MASTER_HOST=127.0.0.1
- REDIS_MASTER_PASSWORD=passwd
- REDIS_PASSWORD=passwd
- REDIS_MASTER_PORT_NUMBER=6379
- REDIS_PORT_NUMBER=6380
depends_on:
- master
network_mode: host

sentinel-1:
image: 'bitnami/redis-sentinel:7.2.1'
environment:
- REDIS_MASTER_HOST=127.0.0.1
- REDIS_MASTER_SET=master
- REDIS_MASTER_PASSWORD=passwd
- REDIS_SENTINEL_PASSWORD=passwd
- REDIS_SENTINEL_DOWN_AFTER_MILLISECONDS=30000
- REDIS_SENTINEL_PORT_NUMBER=26379
depends_on:
- master
- replica
network_mode: host

sentinel-2:
image: 'bitnami/redis-sentinel:7.2.1'
environment:
- REDIS_MASTER_HOST=127.0.0.1
- REDIS_MASTER_SET=master
- REDIS_MASTER_PASSWORD=passwd
- REDIS_SENTINEL_PASSWORD=passwd
- REDIS_SENTINEL_DOWN_AFTER_MILLISECONDS=30000
- REDIS_SENTINEL_PORT_NUMBER=26380
depends_on:
- master
- replica
network_mode: host

sentinel-3:
image: 'bitnami/redis-sentinel:7.2.1'
environment:
- REDIS_MASTER_HOST=127.0.0.1
- REDIS_MASTER_SET=master
- REDIS_MASTER_PASSWORD=passwd
- REDIS_SENTINEL_PASSWORD=passwd
- REDIS_SENTINEL_DOWN_AFTER_MILLISECONDS=30000
- REDIS_SENTINEL_PORT_NUMBER=26381
depends_on:
- master
- replica
network_mode: host
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,12 @@ public VistasRedisClient(Configuration configuration) {
// setup handler
packetEvents = new DefaultPacketHandler();
packetEvents.setSelfId(Vistas.getInstance().getServerId());
OpenAudioLogger.toConsole("Connecting to redis server: " + configuration.getString(StorageKey.REDIS_HOST));
redis = new SimpleRedisClient(
configuration.getString(StorageKey.REDIS_HOST),
configuration.getInt(StorageKey.REDIS_PORT),
configuration.getString(StorageKey.REDIS_PASSWORD),
configuration.getBoolean(StorageKey.REDIS_USE_SSL),
configuration.getString(StorageKey.REDIS_SENTINEL_MASTER_SET),
packetEvents,
"deputy_to_server"
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.craftmend.openaudiomc.vistas.client.redis;

import com.craftmend.openaudiomc.generic.logging.OpenAudioLogger;
import com.craftmend.openaudiomc.generic.storage.enums.StorageKey;
import com.craftmend.openaudiomc.vistas.client.utils.RedisUtils;
import io.lettuce.core.ClientOptions;
import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
Expand All @@ -14,13 +17,35 @@ public class RedisConnection {
@Getter private StatefulRedisPubSubConnection<String, String> connection;
@Getter private RedisPubSubAsyncCommands<String, String> pubSubHandler;

public RedisConnection(String host, int port, String password) {
RedisURI uri = RedisURI.builder()
.withPassword(password)
.withHost(host)
.withPort(port)
.withSsl(false)
.build();
public RedisConnection(String host,
int port,
String password,
boolean useSSL,
String sentinelMasterSet
) {
// Read Redis password
final char[] pass = password.isEmpty() || password.equals("none") ? null : password.toCharArray();
RedisURI uri;

if (sentinelMasterSet.isEmpty()) {
uri = RedisURI.builder()
.withPassword(pass)
.withHost(host)
.withPort(port)
.withSsl(useSSL)
.build();
} else {
final RedisURI.Builder builder = RedisURI.builder()
.withPassword(pass)
.withSsl(useSSL)
.withSentinelMasterId(sentinelMasterSet);
for (final String h : host.split(",")) {
builder.withSentinel(RedisUtils.readRedisUri(h, 26379));
}
uri = builder.build();
}

OpenAudioLogger.toConsole("Connecting to redis server: " + uri.toString());

redisClient = RedisClient.create(uri);
redisClient.setOptions(ClientOptions.builder().autoReconnect(true).build());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,18 @@ public class SimpleRedisClient extends RedisPubSubAdapter<String, String> {
private RedisConnection senderConnection;
private RedisConnection listenerConnection;

public SimpleRedisClient(String host, int port, String password, IRedisHandler handler, String... channels) {
public SimpleRedisClient(String host,
int port,
String password,
boolean useSSL,
String sentinelMasterSet,
IRedisHandler handler,
String... channels
) {
this.handler = handler;
this.senderConnection = new RedisConnection(host, port, password)
this.senderConnection = new RedisConnection(host, port, password, useSSL, sentinelMasterSet)
.connectPubSub();
this.listenerConnection = new RedisConnection(host, port, password)
this.listenerConnection = new RedisConnection(host, port, password, useSSL, sentinelMasterSet)
.connectPubSub();

// listen to packets from Spigot To Deputy
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,12 @@ public class VistasRedisServer extends Service {
public VistasRedisServer(Configuration configuration) {
// setup handler
packetEvents = new DefaultPacketHandler();
OpenAudioLogger.toConsole("Connecting to redis server: " + configuration.getString(StorageKey.REDIS_HOST));
redis = new SimpleRedisClient(
configuration.getString(StorageKey.REDIS_HOST),
configuration.getInt(StorageKey.REDIS_PORT),
configuration.getString(StorageKey.REDIS_PASSWORD),
configuration.getBoolean(StorageKey.REDIS_USE_SSL),
configuration.getString(StorageKey.REDIS_SENTINEL_MASTER_SET),
packetEvents,
"server_to_deputy"
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.craftmend.openaudiomc.vistas.client.utils;

import io.lettuce.core.RedisURI;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public final class RedisUtils {

private static final Pattern URI_FORMAT = Pattern.compile("^(?:(.+)@)?([a-zA-Z0-9.]+)(?::([0-9]{1,5}))?$");

private RedisUtils() {
}

public static RedisURI readRedisUri(String str, int defaultPort) {
final Matcher matcher = URI_FORMAT.matcher(str);
if (!matcher.matches())
return null;
return RedisURI.builder()
.withHost(matcher.group(2))
.withPort(matcher.group(3) == null ? defaultPort : Integer.parseInt(matcher.group(3)))
.withPassword(matcher.group(1) == null ? null : matcher.group(1).toCharArray())
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ public void handleMigrations() {
new TokenMessageMigration(), // adds the token message
new AddVehicleRegionConfigMigration(), // adds the option to disable regions while in a vehicle
new VoicechatDeafenMigration(), // adds the option to deafen yourself
new AddRedisSentinelMigration(), // adds the option to use redis sentinel
};

for (SimpleMigration migration : migrations) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.craftmend.openaudiomc.generic.migrations.migrations;

import com.craftmend.openaudiomc.OpenAudioMc;
import com.craftmend.openaudiomc.generic.migrations.MigrationWorker;
import com.craftmend.openaudiomc.generic.migrations.interfaces.SimpleMigration;
import com.craftmend.openaudiomc.generic.storage.enums.StorageKey;
import com.craftmend.openaudiomc.generic.storage.interfaces.Configuration;

public class AddRedisSentinelMigration extends SimpleMigration {

@Override
public boolean shouldBeRun(MigrationWorker migrationWorker) {
Configuration config = OpenAudioMc.getInstance().getConfiguration();
return !config.hasStorageKey(StorageKey.REDIS_SENTINEL_MASTER_SET);
}

@Override
public void execute(MigrationWorker migrationWorker) {
migrateFilesFromResources();
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,18 @@
import com.craftmend.openaudiomc.OpenAudioMc;
import com.craftmend.openaudiomc.generic.commands.CommandService;
import com.craftmend.openaudiomc.generic.commands.subcommands.RedisSubCommand;
import com.craftmend.openaudiomc.generic.platform.interfaces.TaskService;
import com.craftmend.openaudiomc.generic.service.Inject;
import com.craftmend.openaudiomc.generic.service.Service;
import com.craftmend.openaudiomc.generic.storage.interfaces.Configuration;
import com.craftmend.openaudiomc.generic.logging.OpenAudioLogger;
import com.craftmend.openaudiomc.generic.platform.interfaces.TaskService;
import com.craftmend.openaudiomc.generic.redis.packets.ExecuteBulkCommandsPacket;
import com.craftmend.openaudiomc.generic.redis.packets.ExecuteCommandPacket;
import com.craftmend.openaudiomc.generic.redis.packets.channels.ChannelKey;
import com.craftmend.openaudiomc.generic.redis.packets.interfaces.OARedisPacket;
import com.craftmend.openaudiomc.generic.redis.packets.models.WaitingPacket;
import com.craftmend.openaudiomc.generic.service.Inject;
import com.craftmend.openaudiomc.generic.service.Service;
import com.craftmend.openaudiomc.generic.storage.enums.StorageKey;
import com.craftmend.openaudiomc.generic.storage.interfaces.Configuration;
import com.craftmend.openaudiomc.generic.utils.redis.RedisUtils;
import io.lettuce.core.ClientOptions;
import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
Expand All @@ -32,7 +33,6 @@ public class RedisService extends Service {
// dependencies
@Inject
private Configuration Configuration;

private RedisClient redisPub;
private RedisClient redisSub;
private RedisURI uri;
Expand Down Expand Up @@ -82,21 +82,30 @@ public void onEnable() {

OpenAudioLogger.toConsole("Enabling redis service..");

if (Configuration.getString(StorageKey.REDIS_PASSWORD).equals("none")) {
// Read Redis password
final String redisPass = Configuration.getString(StorageKey.REDIS_PASSWORD);
final char[] password = redisPass.isEmpty() || redisPass.equals("none") ? null : redisPass.toCharArray();

if (Configuration.getString(StorageKey.REDIS_SENTINEL_MASTER_SET).isEmpty()) {
uri = RedisURI.builder()
.withPassword(password)
.withHost(Configuration.getString(StorageKey.REDIS_HOST))
.withPort(Configuration.getInt(StorageKey.REDIS_PORT))
.withSsl(Configuration.getBoolean(StorageKey.REDIS_USE_SSL))
.build();
} else {
uri = RedisURI.builder()
.withPassword(Configuration.getString(StorageKey.REDIS_PASSWORD))
.withHost(Configuration.getString(StorageKey.REDIS_HOST))
.withPort(Configuration.getInt(StorageKey.REDIS_PORT))
final RedisURI.Builder builder = RedisURI.builder()
.withPassword(password)
.withSsl(Configuration.getBoolean(StorageKey.REDIS_USE_SSL))
.build();
.withSentinelMasterId(Configuration.getString(StorageKey.REDIS_SENTINEL_MASTER_SET));
for (final String host : Configuration.getString(StorageKey.REDIS_HOST).split(",")) {
builder.withSentinel(RedisUtils.readRedisUri(host, 26379));
}
uri = builder.build();
}

OpenAudioLogger.toConsole("Connecting to redis server: " + uri.toString());

// set up listener
redisSub = RedisClient.create(uri);
redisSub.setOptions(ClientOptions.builder().autoReconnect(true).build());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ public enum StorageKey {
REDIS_PASSWORD(false, "redis.password", StorageLocation.CONFIG_FILE),
REDIS_USE_SSL(false, "redis.useSSL", StorageLocation.CONFIG_FILE),
REDIS_SECTION(false, "redis.section", StorageLocation.CONFIG_FILE),
REDIS_SENTINEL_MASTER_SET(false, "redis.sentinel-master-set", StorageLocation.CONFIG_FILE),

CDN_PREFERRED_PORT(false, "cdn.preferred-bridge-port", StorageLocation.CONFIG_FILE),
CDN_TIMEOUT(false, "cdn.timeout-seconds", StorageLocation.CONFIG_FILE),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.craftmend.openaudiomc.generic.utils.redis;

import io.lettuce.core.RedisURI;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public final class RedisUtils {

private static final Pattern URI_FORMAT = Pattern.compile("^(?:(.+)@)?([a-zA-Z0-9.]+)(?::([0-9]{1,5}))?$");

private RedisUtils() {
}

public static RedisURI readRedisUri(String str, int defaultPort) {
final Matcher matcher = URI_FORMAT.matcher(str);
if (!matcher.matches())
return null;
return RedisURI.builder()
.withHost(matcher.group(2))
.withPort(matcher.group(3) == null ? defaultPort : Integer.parseInt(matcher.group(3)))
.withPassword(matcher.group(1) == null ? null : matcher.group(1).toCharArray())
.build();
}
}
2 changes: 2 additions & 0 deletions plugin/src/main/resources/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -244,3 +244,5 @@ redis:
useSSL: false
# The channel to sync with. When configured, OpenAudioMc will only sync to servers on redis with the same section
section: event
# When using Redis Sentinel, you should set the master set to use. Leaves empty to use a single server Redis cluster
sentinel-master-set: ''