From 44f9a0945ddb18e73335c63a0196d0361cbf3068 Mon Sep 17 00:00:00 2001 From: mohammed jasem alaajel Date: Sat, 16 Jul 2022 09:18:33 +0400 Subject: [PATCH] API converted to support RedisCluster --- .../minecraft/redisbungee/RedisBungeeAPI.java | 58 ++++- .../internal/AbstractDataManager.java | 80 +++--- .../redisbungee/internal/PubSubListener.java | 62 +++-- .../internal/RedisBungeePlugin.java | 11 +- .../summoners/ClusterJedisSummoner.java | 30 +++ .../internal/summoners/JedisSummoner.java | 36 ++- .../summoners/SinglePoolJedisSummoner.java | 34 --- .../internal/summoners/Summoner.java | 20 ++ .../redisbungee/internal/util/LuaManager.java | 62 +++-- .../internal/util/RedisBungeeMode.java | 5 + .../internal/util/RedisCallable.java | 48 ---- .../redisbungee/internal/util/RedisTask.java | 50 ++++ .../internal/util/uuid/UUIDTranslator.java | 238 ++++++++++++------ .../RedisBungeeBungeeListener.java | 7 +- .../redisbungee/RedisBungeeBungeePlugin.java | 6 +- .../RedisBungeeVelocityListener.java | 1 - .../RedisBungeeVelocityPlugin.java | 6 +- 17 files changed, 501 insertions(+), 253 deletions(-) create mode 100644 RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/summoners/ClusterJedisSummoner.java delete mode 100644 RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/summoners/SinglePoolJedisSummoner.java create mode 100644 RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/summoners/Summoner.java create mode 100644 RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/util/RedisBungeeMode.java delete mode 100644 RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/util/RedisCallable.java create mode 100644 RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/util/RedisTask.java diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeAPI.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeAPI.java index 4cb2db4..6094d19 100644 --- a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeAPI.java +++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/RedisBungeeAPI.java @@ -6,6 +6,8 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Multimap; import com.imaginarycode.minecraft.redisbungee.internal.RedisBungeePlugin; import com.imaginarycode.minecraft.redisbungee.internal.summoners.JedisSummoner; +import com.imaginarycode.minecraft.redisbungee.internal.summoners.Summoner; +import com.imaginarycode.minecraft.redisbungee.internal.util.RedisBungeeMode; import org.checkerframework.checker.nullness.qual.NonNull; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; @@ -311,30 +313,60 @@ public class RedisBungeeAPI { * @since 0.7.0 */ public Jedis requestJedis() { - return this.plugin.getJedisSummoner().requestJedis(); + if (getMode() == RedisBungeeMode.SINGLE) { + return ((JedisSummoner) this.plugin.getSummoner()).obtainResource(); + } else { + throw new RuntimeException("RedisBungee is on Cluster MODE!"); + } } - /** * This gets Redis Bungee {@link JedisPool} * @return {@link JedisPool} * @since 0.6.5 */ public JedisPool getJedisPool() { - return this.plugin.getJedisSummoner().getJedisPool(); + if (getMode() == RedisBungeeMode.SINGLE) { + return ((JedisSummoner) this.plugin.getSummoner()).getJedisPool(); + } else { + throw new RuntimeException("RedisBungee is on Cluster MODE!"); + } } /** - * This gets Redis Bungee {@link JedisPool} - * @return {@link JedisPool} - * @since 0.6.5 - */ - public JedisSummoner getJedisSummoner() { - return this.plugin.getJedisSummoner(); - } - - - /** + * returns Summoner class responsible for Single Jedis {@link Jedis}, Cluster Jedis {@link redis.clients.jedis.JedisCluster} handling * + * @return {@link Summoner} + * @since 0.8.0 + */ + public Summoner getSummoner() { + return this.plugin.getSummoner(); + } + + /** + * This gives you instance of Jedis Cluster + * @return {@link redis.clients.jedis.JedisCluster} + * @since 0.8.0 + */ + public Jedis requestClusterJedis() { + if (getMode() == RedisBungeeMode.CLUSTER) { + return ((JedisSummoner) this.plugin.getSummoner()).obtainResource(); + } else { + throw new RuntimeException("RedisBungee is on single MODE!"); + } + } + + /** + * shows what mode is RedisBungee is on + * @return {@link RedisBungeeMode} + * @since 0.8.0 + */ + public RedisBungeeMode getMode() { + return this.plugin.getRedisBungeeMode(); + } + + + /** + * Api instance * @return the API instance. * @since 0.6.5 */ diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/AbstractDataManager.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/AbstractDataManager.java index 4a9d91b..88d941d 100644 --- a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/AbstractDataManager.java +++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/AbstractDataManager.java @@ -8,7 +8,9 @@ import com.google.common.util.concurrent.UncheckedExecutionException; import com.google.gson.Gson; import com.google.gson.JsonObject; import com.google.gson.JsonParser; +import com.imaginarycode.minecraft.redisbungee.internal.util.RedisTask; import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisCluster; import java.lang.reflect.InvocationTargetException; import java.net.InetAddress; @@ -52,12 +54,17 @@ public abstract class AbstractDataManager { return plugin.isPlayerOnAServer(player) ? plugin.getPlayerServerName(player) : null; try { - return serverCache.get(uuid, new Callable() { + return serverCache.get(uuid, new RedisTask(plugin.getApi()) { @Override - public String call() throws Exception { - try (Jedis tmpRsc = plugin.getJedisSummoner().requestJedis()) { - return Objects.requireNonNull(tmpRsc.hget("player:" + uuid, "server"), "user not found"); - } + public String singleJedisTask(Jedis jedis) { + return Objects.requireNonNull(jedis.hget("player:" + uuid, "server"), "user not found"); + + } + + @Override + public String clusterJedisTask(JedisCluster jedisCluster) { + return Objects.requireNonNull(jedisCluster.hget("player:" + uuid, "server"), "user not found"); + } }); } catch (ExecutionException | UncheckedExecutionException e) { @@ -76,12 +83,15 @@ public abstract class AbstractDataManager { return plugin.getConfiguration().getServerId(); try { - return proxyCache.get(uuid, new Callable() { + return proxyCache.get(uuid, new RedisTask(plugin.getApi()) { @Override - public String call() throws Exception { - try (Jedis tmpRsc = plugin.getJedisSummoner().requestJedis()) { - return Objects.requireNonNull(tmpRsc.hget("player:" + uuid, "proxy"), "user not found"); - } + public String singleJedisTask(Jedis jedis) { + return Objects.requireNonNull(jedis.hget("player:" + uuid, "proxy"), "user not found"); + } + + @Override + public String clusterJedisTask(JedisCluster jedisCluster) { + return Objects.requireNonNull(jedisCluster.hget("player:" + uuid, "proxy"), "user not found"); } }); } catch (ExecutionException | UncheckedExecutionException e) { @@ -99,15 +109,21 @@ public abstract class AbstractDataManager { return plugin.getPlayerIp(player); try { - return ipCache.get(uuid, new Callable() { + return ipCache.get(uuid, new RedisTask(plugin.getApi()) { @Override - public InetAddress call() throws Exception { - try (Jedis tmpRsc = plugin.getJedisSummoner().requestJedis()) { - String result = tmpRsc.hget("player:" + uuid, "ip"); - if (result == null) - throw new NullPointerException("user not found"); - return InetAddresses.forString(result); - } + public InetAddress singleJedisTask(Jedis jedis) { + String result = jedis.hget("player:" + uuid, "ip"); + if (result == null) + throw new NullPointerException("user not found"); + return InetAddresses.forString(result); + } + + @Override + public InetAddress clusterJedisTask(JedisCluster jedisCluster) { + String result = jedisCluster.hget("player:" + uuid, "ip"); + if (result == null) + throw new NullPointerException("user not found"); + return InetAddresses.forString(result); } }); } catch (ExecutionException | UncheckedExecutionException e) { @@ -125,13 +141,17 @@ public abstract class AbstractDataManager { return 0; try { - return lastOnlineCache.get(uuid, new Callable() { + return lastOnlineCache.get(uuid, new RedisTask(plugin.getApi()) { @Override - public Long call() throws Exception { - try (Jedis tmpRsc = plugin.getJedisSummoner().requestJedis()) { - String result = tmpRsc.hget("player:" + uuid, "online"); - return result == null ? -1 : Long.valueOf(result); - } + public Long singleJedisTask(Jedis jedis) { + String result = jedis.hget("player:" + uuid, "online"); + return result == null ? -1 : Long.parseLong(result); + } + + @Override + public Long clusterJedisTask(JedisCluster jedisCluster) { + String result = jedisCluster.hget("player:" + uuid, "online"); + return result == null ? -1 : Long.parseLong(result); } }); } catch (ExecutionException e) { @@ -149,6 +169,7 @@ public abstract class AbstractDataManager { // Invalidate all entries related to this player, since they now lie. (call invalidate(uuid)) public abstract void onPostLogin(PL event); + // Invalidate all entries related to this player, since they now lie. (call invalidate(uuid)) public abstract void onPlayerDisconnect(PD event); @@ -181,7 +202,8 @@ public abstract class AbstractDataManager { Object event; try { event = plugin.getNetworkJoinEventClass().getDeclaredConstructor(UUID.class).newInstance(message1.getTarget()); - } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | + NoSuchMethodException e) { throw new RuntimeException("unable to dispatch an network join event", e); } plugin.callEvent(event); @@ -200,7 +222,8 @@ public abstract class AbstractDataManager { Object event; try { event = plugin.getNetworkQuitEventClass().getDeclaredConstructor(UUID.class).newInstance(message2.getTarget()); - } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | + NoSuchMethodException e) { throw new RuntimeException("unable to dispatch an network quit event", e); } plugin.callEvent(event); @@ -217,7 +240,8 @@ public abstract class AbstractDataManager { Object event; try { event = plugin.getServerChangeEventClass().getDeclaredConstructor(UUID.class, String.class, String.class).newInstance(message3.getTarget(), ((ServerChangePayload) message3.getPayload()).getOldServer(), ((ServerChangePayload) message3.getPayload()).getServer()); - } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | + NoSuchMethodException e) { throw new RuntimeException("unable to dispatch an server change event", e); } plugin.callEvent(event); @@ -275,7 +299,7 @@ public abstract class AbstractDataManager { } } - public static class ServerChangePayload{ + public static class ServerChangePayload { private final String server; private final String oldServer; diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/PubSubListener.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/PubSubListener.java index 9e75aa2..3442db2 100644 --- a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/PubSubListener.java +++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/PubSubListener.java @@ -1,13 +1,14 @@ package com.imaginarycode.minecraft.redisbungee.internal; +import com.imaginarycode.minecraft.redisbungee.internal.util.RedisTask; import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisCluster; import redis.clients.jedis.exceptions.JedisConnectionException; import java.util.Arrays; import java.util.HashSet; import java.util.Set; import java.util.concurrent.TimeUnit; -import java.util.logging.Level; public class PubSubListener implements Runnable { private JedisPubSubHandler jpsh; @@ -21,27 +22,58 @@ public class PubSubListener implements Runnable { @Override public void run() { - try (Jedis rsc = plugin.getJedisSummoner().requestJedis()) { - try { - - jpsh = new JedisPubSubHandler(plugin); - addedChannels.add("redisbungee-" + plugin.getConfiguration().getServerId()); - addedChannels.add("redisbungee-allservers"); - addedChannels.add("redisbungee-data"); - rsc.subscribe(jpsh, addedChannels.toArray(new String[0])); - } catch (Exception e) { - // FIXME: Extremely ugly hack - // Attempt to unsubscribe this instance and try again. - plugin.logWarn("PubSub error, attempting to recover."); + RedisTask subTask = new RedisTask(plugin.getApi()) { + @Override + public Void singleJedisTask(Jedis jedis) { try { - jpsh.unsubscribe(); - } catch (Exception e1) { + jpsh = new JedisPubSubHandler(plugin); + addedChannels.add("redisbungee-" + plugin.getConfiguration().getServerId()); + addedChannels.add("redisbungee-allservers"); + addedChannels.add("redisbungee-data"); + jedis.subscribe(jpsh, addedChannels.toArray(new String[0])); + } catch (Exception e) { + // FIXME: Extremely ugly hack + // Attempt to unsubscribe this instance and try again. + plugin.logWarn("PubSub error, attempting to recover."); + try { + jpsh.unsubscribe(); + } catch (Exception e1) { /* This may fail with - java.net.SocketException: Broken pipe - redis.clients.jedis.exceptions.JedisConnectionException: JedisPubSub was not subscribed to a Jedis instance */ + } } + return null; } + + @Override + public Void clusterJedisTask(JedisCluster jedisCluster) { + try { + jpsh = new JedisPubSubHandler(plugin); + addedChannels.add("redisbungee-" + plugin.getConfiguration().getServerId()); + addedChannels.add("redisbungee-allservers"); + addedChannels.add("redisbungee-data"); + jedisCluster.subscribe(jpsh, addedChannels.toArray(new String[0])); + } catch (Exception e) { + // FIXME: Extremely ugly hack + // Attempt to unsubscribe this instance and try again. + plugin.logWarn("PubSub error, attempting to recover."); + try { + jpsh.unsubscribe(); + } catch (Exception e1) { + /* This may fail with + - java.net.SocketException: Broken pipe + - redis.clients.jedis.exceptions.JedisConnectionException: JedisPubSub was not subscribed to a Jedis instance + */ + } + } + return null; + } + }; + + try { + subTask.execute(); } catch (JedisConnectionException e) { plugin.logWarn("PubSub error, attempting to recover in 5 secs."); plugin.executeAsyncAfter(this, TimeUnit.SECONDS, 5); diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/RedisBungeePlugin.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/RedisBungeePlugin.java index 6dd70ec..9041088 100644 --- a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/RedisBungeePlugin.java +++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/RedisBungeePlugin.java @@ -2,7 +2,8 @@ package com.imaginarycode.minecraft.redisbungee.internal; import com.google.common.collect.Multimap; import com.imaginarycode.minecraft.redisbungee.RedisBungeeAPI; -import com.imaginarycode.minecraft.redisbungee.internal.summoners.JedisSummoner; +import com.imaginarycode.minecraft.redisbungee.internal.summoners.Summoner; +import com.imaginarycode.minecraft.redisbungee.internal.util.RedisBungeeMode; import com.imaginarycode.minecraft.redisbungee.internal.util.uuid.UUIDTranslator; import redis.clients.jedis.Jedis; @@ -31,11 +32,7 @@ public interface RedisBungeePlugin

extends EventsPlatform{ } - Jedis requestJedis(); - - boolean isJedisAvailable(); - - JedisSummoner getJedisSummoner(); + Summoner getSummoner(); RedisBungeeConfiguration getConfiguration(); @@ -101,4 +98,6 @@ public interface RedisBungeePlugin

extends EventsPlatform{ void loadConfig() throws Exception; + RedisBungeeMode getRedisBungeeMode(); + } diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/summoners/ClusterJedisSummoner.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/summoners/ClusterJedisSummoner.java new file mode 100644 index 0000000..fbcb786 --- /dev/null +++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/summoners/ClusterJedisSummoner.java @@ -0,0 +1,30 @@ +package com.imaginarycode.minecraft.redisbungee.internal.summoners; + +import redis.clients.jedis.JedisCluster; + +import java.io.IOException; + +public class ClusterJedisSummoner implements Summoner { + public final JedisCluster jedisCluster; + private boolean closed = false; + + public ClusterJedisSummoner(JedisCluster jedisCluster) { + this.jedisCluster = jedisCluster; + } + + @Override + public JedisCluster obtainResource() { + return jedisCluster; + } + + @Override + public boolean isAvailable() { + return !closed; + } + + @Override + public void close() throws IOException { + this.closed = true; + jedisCluster.close(); + } +} diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/summoners/JedisSummoner.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/summoners/JedisSummoner.java index 9c51033..bb725d8 100644 --- a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/summoners/JedisSummoner.java +++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/summoners/JedisSummoner.java @@ -1,25 +1,35 @@ package com.imaginarycode.minecraft.redisbungee.internal.summoners; import redis.clients.jedis.Jedis; -import redis.clients.jedis.JedisCluster; import redis.clients.jedis.JedisPool; -import java.io.Closeable; +import java.io.IOException; +public class JedisSummoner implements Summoner { -/** - * This class intended for future release to support redis sentinel or redis clusters - * - * @author Ham1255 - * @since 0.7.0 - * - */ -public interface JedisSummoner extends Closeable { + private final JedisPool jedisPool; - Jedis requestJedis(); + public JedisSummoner(JedisPool jedisPool) { + this.jedisPool = jedisPool; + } - boolean isJedisAvailable(); + @Override + public Jedis obtainResource() { + return jedisPool.getResource(); + } - JedisPool getJedisPool(); + public JedisPool getJedisPool() { + return this.jedisPool; + } + @Override + public boolean isAvailable() { + return !jedisPool.isClosed(); + } + + @Override + public void close() throws IOException { + this.jedisPool.close(); + + } } diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/summoners/SinglePoolJedisSummoner.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/summoners/SinglePoolJedisSummoner.java deleted file mode 100644 index 02f4453..0000000 --- a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/summoners/SinglePoolJedisSummoner.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.imaginarycode.minecraft.redisbungee.internal.summoners; - -import redis.clients.jedis.Jedis; -import redis.clients.jedis.JedisPool; - -import java.io.IOException; - -public class SinglePoolJedisSummoner implements JedisSummoner { - final JedisPool jedisPool; - - public SinglePoolJedisSummoner(JedisPool jedisPool) { - this.jedisPool = jedisPool; - } - - @Override - public Jedis requestJedis() { - return this.jedisPool.getResource(); - } - - @Override - public boolean isJedisAvailable() { - return !this.jedisPool.isClosed(); - } - - @Override - public JedisPool getJedisPool() { - return this.jedisPool; - } - - @Override - public void close() throws IOException { - jedisPool.close(); - } -} diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/summoners/Summoner.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/summoners/Summoner.java new file mode 100644 index 0000000..b2e3d55 --- /dev/null +++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/summoners/Summoner.java @@ -0,0 +1,20 @@ +package com.imaginarycode.minecraft.redisbungee.internal.summoners; + +import java.io.Closeable; + + +/** + * This class intended for future release to support redis sentinel or redis clusters + * + * @author Ham1255 + * @since 0.7.0 + * + */ +public interface Summoner

extends Closeable { + + P obtainResource(); + + boolean isAvailable(); + + +} diff --git a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/util/LuaManager.java b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/util/LuaManager.java index 52f8ebb..77dba56 100644 --- a/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/util/LuaManager.java +++ b/RedisBungee-API/src/main/java/com/imaginarycode/minecraft/redisbungee/internal/util/LuaManager.java @@ -2,6 +2,7 @@ package com.imaginarycode.minecraft.redisbungee.internal.util; import com.imaginarycode.minecraft.redisbungee.internal.RedisBungeePlugin; import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisCluster; import redis.clients.jedis.exceptions.JedisDataException; import java.util.List; @@ -14,10 +15,20 @@ public class LuaManager { } public Script createScript(String script) { - try (Jedis jedis = plugin.getJedisSummoner().requestJedis()) { - String hash = jedis.scriptLoad(script); - return new Script(script, hash); - } + RedisTask