Add support for master-slave replication in redis to balance load of read and writes. Redis based repositories now read/write to specified connection pools (master or slave).

This commit is contained in:
Ty Sayers 2015-03-07 23:19:35 -05:00
parent e0ba667613
commit 4237cd082c
8 changed files with 160 additions and 94 deletions

View File

@ -12,6 +12,7 @@ import mineplex.core.report.command.ReportNotification;
import mineplex.serverdata.DataRepository;
import mineplex.serverdata.RedisDataRepository;
import mineplex.serverdata.Region;
import mineplex.serverdata.Utility;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
@ -189,7 +190,7 @@ public class ReportManager {
*/
public int generateReportId()
{
JedisPool pool = ((RedisDataRepository<Report>) reportRepository).getJedisPool();
JedisPool pool = Utility.getPool(true);
Jedis jedis = pool.getResource();
long uniqueReportId = -1;

View File

@ -9,6 +9,7 @@ import java.util.Set;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import mineplex.serverdata.ConnectionData;
import mineplex.serverdata.DataRepository;
import mineplex.serverdata.MinecraftServer;
import mineplex.serverdata.RedisDataRepository;
@ -29,9 +30,9 @@ public class QueueRepository
* @param port - the designated port of the QueueRepository database
* @param region - the region of server queues to manage
*/
public QueueRepository(String host, int port, Region region)
public QueueRepository(ConnectionData connectionData, Region region)
{
this._partyRepository = new RedisDataRepository<QueueParty>(host, port, region,
this._partyRepository = new RedisDataRepository<QueueParty>(connectionData, region,
QueueParty.class, "queue-parties");
}
@ -43,7 +44,7 @@ public class QueueRepository
*/
public QueueRepository(Region region)
{
this(ServerManager.DEFAULT_REDIS_HOST, ServerManager.DEFAULT_REDIS_PORT, region);
this(ServerManager.DEFAULT_MASTER_CONNECTION, region);
}
public QueueParty getQueueParty(int partyId)

View File

@ -0,0 +1,27 @@
package mineplex.serverdata;
/**
* ConnectionData stores information relevant for initiating a connection to a repository.
* @author MrTwiggy
*
*/
public class ConnectionData
{
private String _host; // The host URL to connect to repository
public String getHost() { return _host; }
private int _port; // The port to connect to repository
public int getPort() { return _port; }
/**
* Constructor
* @param host - the host URL defining the repository
* @param port - the port used for connection to repository
*/
public ConnectionData(String host, int port)
{
_host = host;
_port = port;
}
}

View File

@ -21,8 +21,8 @@ public class RedisDataRepository<T extends Data> implements DataRepository<T>
public final char KEY_DELIMITER = '.';
// The pool used to retrieve jedis instances.
private JedisPool _jedisPool;
public JedisPool getJedisPool() { return _jedisPool; }
private JedisPool _writePool;
private JedisPool _readPool;
// The geographical region of the servers stored by this ServerRepository
private Region _region;
@ -33,22 +33,30 @@ public class RedisDataRepository<T extends Data> implements DataRepository<T>
/**
* Class constructor
* @param writeConn
* @param readConn
* @param host
* @param port
* @param region
*/
public RedisDataRepository(String host, int port, Region region,
public RedisDataRepository(ConnectionData writeConn, ConnectionData readConn, Region region,
Class<T> elementType, String elementLabel)
{
_jedisPool = new JedisPool(new JedisPoolConfig(), host, port);
_writePool = Utility.generatePool(writeConn);
_readPool = (writeConn == readConn) ? _writePool : Utility.generatePool(readConn);
_region = region;
_elementType = elementType;
_elementLabel = elementLabel;
}
public RedisDataRepository(ConnectionData conn, Region region, Class<T> elementType, String elementLabel)
{
this(conn, conn, region, elementType, elementLabel);
}
public RedisDataRepository(Region region, Class<T> elementType, String elementLabel)
{
this(ServerManager.DEFAULT_REDIS_HOST, ServerManager.DEFAULT_REDIS_PORT, region,
this(ServerManager.DEFAULT_MASTER_CONNECTION, ServerManager.DEFAULT_SLAVE_CONNECTION, region,
elementType, elementLabel);
}
@ -71,7 +79,7 @@ public class RedisDataRepository<T extends Data> implements DataRepository<T>
public Collection<T> getElements()
{
Collection<T> elements = new HashSet<T>();
Jedis jedis = _jedisPool.getResource();
Jedis jedis = _readPool.getResource();
try
{
@ -99,14 +107,14 @@ public class RedisDataRepository<T extends Data> implements DataRepository<T>
catch (JedisConnectionException exception)
{
exception.printStackTrace();
_jedisPool.returnBrokenResource(jedis);
_readPool.returnBrokenResource(jedis);
jedis = null;
}
finally
{
if (jedis != null)
{
_jedisPool.returnResource(jedis);
_readPool.returnResource(jedis);
}
}
@ -117,7 +125,7 @@ public class RedisDataRepository<T extends Data> implements DataRepository<T>
public T getElement(String dataId)
{
T element = null;
Jedis jedis = _jedisPool.getResource();
Jedis jedis = _readPool.getResource();
try
{
@ -128,14 +136,14 @@ public class RedisDataRepository<T extends Data> implements DataRepository<T>
catch (JedisConnectionException exception)
{
exception.printStackTrace();
_jedisPool.returnBrokenResource(jedis);
_readPool.returnBrokenResource(jedis);
jedis = null;
}
finally
{
if (jedis != null)
{
_jedisPool.returnResource(jedis);
_readPool.returnResource(jedis);
}
}
@ -145,7 +153,7 @@ public class RedisDataRepository<T extends Data> implements DataRepository<T>
@Override
public void addElement(T element, int timeout)
{
Jedis jedis = _jedisPool.getResource();
Jedis jedis = _writePool.getResource();
try
{
@ -163,14 +171,14 @@ public class RedisDataRepository<T extends Data> implements DataRepository<T>
catch (JedisConnectionException exception)
{
exception.printStackTrace();
_jedisPool.returnBrokenResource(jedis);
_writePool.returnBrokenResource(jedis);
jedis = null;
}
finally
{
if (jedis != null)
{
_jedisPool.returnResource(jedis);
_writePool.returnResource(jedis);
}
}
}
@ -190,7 +198,7 @@ public class RedisDataRepository<T extends Data> implements DataRepository<T>
@Override
public void removeElement(String dataId)
{
Jedis jedis = _jedisPool.getResource();
Jedis jedis = _writePool.getResource();
try
{
@ -205,14 +213,14 @@ public class RedisDataRepository<T extends Data> implements DataRepository<T>
catch (JedisConnectionException exception)
{
exception.printStackTrace();
_jedisPool.returnBrokenResource(jedis);
_writePool.returnBrokenResource(jedis);
jedis = null;
}
finally
{
if (jedis != null)
{
_jedisPool.returnResource(jedis);
_writePool.returnResource(jedis);
}
}
}
@ -226,7 +234,7 @@ public class RedisDataRepository<T extends Data> implements DataRepository<T>
@Override
public int clean()
{
Jedis jedis = _jedisPool.getResource();
Jedis jedis = _writePool.getResource();
try
{
@ -243,14 +251,14 @@ public class RedisDataRepository<T extends Data> implements DataRepository<T>
catch (JedisConnectionException exception)
{
exception.printStackTrace();
_jedisPool.returnBrokenResource(jedis);
_writePool.returnBrokenResource(jedis);
jedis = null;
}
finally
{
if (jedis != null)
{
_jedisPool.returnResource(jedis);
_writePool.returnResource(jedis);
}
}
@ -260,7 +268,7 @@ public class RedisDataRepository<T extends Data> implements DataRepository<T>
protected Set<String> getActiveElements()
{
Set<String> dataIds = new HashSet<String>();
Jedis jedis = _jedisPool.getResource();
Jedis jedis = _readPool.getResource();
try
{
@ -271,14 +279,14 @@ public class RedisDataRepository<T extends Data> implements DataRepository<T>
catch (JedisConnectionException exception)
{
exception.printStackTrace();
_jedisPool.returnBrokenResource(jedis);
_readPool.returnBrokenResource(jedis);
jedis = null;
}
finally
{
if (jedis != null)
{
_jedisPool.returnResource(jedis);
_readPool.returnResource(jedis);
}
}
@ -288,7 +296,7 @@ public class RedisDataRepository<T extends Data> implements DataRepository<T>
protected Set<String> getDeadElements()
{
Set<String> dataIds = new HashSet<String>();
Jedis jedis = _jedisPool.getResource();
Jedis jedis = _readPool.getResource();
try
{
@ -299,14 +307,14 @@ public class RedisDataRepository<T extends Data> implements DataRepository<T>
catch (JedisConnectionException exception)
{
exception.printStackTrace();
_jedisPool.returnBrokenResource(jedis);
_readPool.returnBrokenResource(jedis);
jedis = null;
}
finally
{
if (jedis != null)
{
_jedisPool.returnResource(jedis);
_readPool.returnResource(jedis);
}
}

View File

@ -31,7 +31,8 @@ public class RedisServerRepository implements ServerRepository
public final char KEY_DELIMITER = '.';
// The pool used to retrieve jedis instances.
private JedisPool _jedisPool;
private JedisPool _writePool;
private JedisPool _readPool;
// The geographical region of the servers stored by this ServerRepository
private Region _region;
@ -41,17 +42,18 @@ public class RedisServerRepository implements ServerRepository
* @param host
* @param port
*/
public RedisServerRepository(String host, int port, Region region)
public RedisServerRepository(ConnectionData writeConn, ConnectionData readConn, Region region)
{
this._jedisPool = new JedisPool(new JedisPoolConfig(), host, port);
this._region = region;
_writePool = Utility.generatePool(writeConn);
_readPool = (writeConn == readConn) ? _writePool : Utility.generatePool(readConn);
_region = region;
}
@Override
public Collection<MinecraftServer> getServerStatuses()
{
Collection<MinecraftServer> servers = new HashSet<MinecraftServer>();
Jedis jedis = _jedisPool.getResource();
Jedis jedis = _readPool.getResource();
try
{
@ -81,14 +83,14 @@ public class RedisServerRepository implements ServerRepository
catch (JedisConnectionException exception)
{
exception.printStackTrace();
_jedisPool.returnBrokenResource(jedis);
_readPool.returnBrokenResource(jedis);
jedis = null;
}
finally
{
if (jedis != null)
{
_jedisPool.returnResource(jedis);
_readPool.returnResource(jedis);
}
}
@ -115,7 +117,7 @@ public class RedisServerRepository implements ServerRepository
public MinecraftServer getServerStatus(String serverName)
{
MinecraftServer server = null;
Jedis jedis = _jedisPool.getResource();
Jedis jedis = _readPool.getResource();
try
{
@ -127,14 +129,14 @@ public class RedisServerRepository implements ServerRepository
catch (JedisConnectionException exception)
{
exception.printStackTrace();
_jedisPool.returnBrokenResource(jedis);
_readPool.returnBrokenResource(jedis);
jedis = null;
}
finally
{
if (jedis != null)
{
_jedisPool.returnResource(jedis);
_readPool.returnResource(jedis);
}
}
@ -144,7 +146,7 @@ public class RedisServerRepository implements ServerRepository
@Override
public void updataServerStatus(MinecraftServer serverData, int timeout)
{
Jedis jedis = _jedisPool.getResource();
Jedis jedis = _writePool.getResource();
try
{
@ -162,14 +164,14 @@ public class RedisServerRepository implements ServerRepository
catch (JedisConnectionException exception)
{
exception.printStackTrace();
_jedisPool.returnBrokenResource(jedis);
_writePool.returnBrokenResource(jedis);
jedis = null;
}
finally
{
if (jedis != null)
{
_jedisPool.returnResource(jedis);
_writePool.returnResource(jedis);
}
}
}
@ -177,7 +179,7 @@ public class RedisServerRepository implements ServerRepository
@Override
public void removeServerStatus(MinecraftServer serverData)
{
Jedis jedis = _jedisPool.getResource();
Jedis jedis = _writePool.getResource();
try
{
@ -193,14 +195,14 @@ public class RedisServerRepository implements ServerRepository
catch (JedisConnectionException exception)
{
exception.printStackTrace();
_jedisPool.returnBrokenResource(jedis);
_writePool.returnBrokenResource(jedis);
jedis = null;
}
finally
{
if (jedis != null)
{
_jedisPool.returnResource(jedis);
_writePool.returnResource(jedis);
}
}
}
@ -215,7 +217,7 @@ public class RedisServerRepository implements ServerRepository
public Collection<DedicatedServer> getDedicatedServers()
{
Collection<DedicatedServer> servers = new HashSet<DedicatedServer>();
Jedis jedis = _jedisPool.getResource();
Jedis jedis = _readPool.getResource();
try
{
@ -254,14 +256,14 @@ public class RedisServerRepository implements ServerRepository
catch (JedisConnectionException exception)
{
exception.printStackTrace();
_jedisPool.returnBrokenResource(jedis);
_readPool.returnBrokenResource(jedis);
jedis = null;
}
finally
{
if (jedis != null)
{
_jedisPool.returnResource(jedis);
_readPool.returnResource(jedis);
}
}
@ -272,7 +274,7 @@ public class RedisServerRepository implements ServerRepository
public Collection<ServerGroup> getServerGroups(Collection<MinecraftServer> serverStatuses)
{
Collection<ServerGroup> servers = new HashSet<ServerGroup>();
Jedis jedis = _jedisPool.getResource();
Jedis jedis = _readPool.getResource();
try
{
@ -311,14 +313,14 @@ public class RedisServerRepository implements ServerRepository
catch (JedisConnectionException exception)
{
exception.printStackTrace();
_jedisPool.returnBrokenResource(jedis);
_readPool.returnBrokenResource(jedis);
jedis = null;
}
finally
{
if (jedis != null)
{
_jedisPool.returnResource(jedis);
_readPool.returnResource(jedis);
}
}
@ -333,7 +335,7 @@ public class RedisServerRepository implements ServerRepository
protected Set<String> getActiveNames(String key)
{
Set<String> names = new HashSet<String>();
Jedis jedis = _jedisPool.getResource();
Jedis jedis = _readPool.getResource();
try
{
@ -344,14 +346,14 @@ public class RedisServerRepository implements ServerRepository
catch (JedisConnectionException exception)
{
exception.printStackTrace();
_jedisPool.returnBrokenResource(jedis);
_readPool.returnBrokenResource(jedis);
jedis = null;
}
finally
{
if (jedis != null)
{
_jedisPool.returnResource(jedis);
_readPool.returnResource(jedis);
}
}
@ -365,7 +367,7 @@ public class RedisServerRepository implements ServerRepository
protected Set<String> getDeadNames(String key)
{
Set<String> names = new HashSet<String>();
Jedis jedis = _jedisPool.getResource();
Jedis jedis = _readPool.getResource();
try
{
@ -376,14 +378,14 @@ public class RedisServerRepository implements ServerRepository
catch (JedisConnectionException exception)
{
exception.printStackTrace();
_jedisPool.returnBrokenResource(jedis);
_readPool.returnBrokenResource(jedis);
jedis = null;
}
finally
{
if (jedis != null)
{
_jedisPool.returnResource(jedis);
_readPool.returnResource(jedis);
}
}
@ -404,7 +406,7 @@ public class RedisServerRepository implements ServerRepository
public Collection<MinecraftServer> getDeadServers()
{
Set<MinecraftServer> servers = new HashSet<MinecraftServer>();
Jedis jedis = _jedisPool.getResource();
Jedis jedis = _readPool.getResource();
try
{
@ -434,14 +436,14 @@ public class RedisServerRepository implements ServerRepository
catch (JedisConnectionException exception)
{
exception.printStackTrace();
_jedisPool.returnBrokenResource(jedis);
_readPool.returnBrokenResource(jedis);
jedis = null;
}
finally
{
if (jedis != null)
{
_jedisPool.returnResource(jedis);
_readPool.returnResource(jedis);
}
}
@ -451,7 +453,7 @@ public class RedisServerRepository implements ServerRepository
@Override
public void updateServerGroup(ServerGroup serverGroup)
{
Jedis jedis = _jedisPool.getResource();
Jedis jedis = _writePool.getResource();
try
{
@ -469,14 +471,14 @@ public class RedisServerRepository implements ServerRepository
catch (JedisConnectionException exception)
{
exception.printStackTrace();
_jedisPool.returnBrokenResource(jedis);
_writePool.returnBrokenResource(jedis);
jedis = null;
}
finally
{
if (jedis != null)
{
_jedisPool.returnResource(jedis);
_writePool.returnResource(jedis);
}
}
}
@ -484,7 +486,7 @@ public class RedisServerRepository implements ServerRepository
@Override
public void removeServerGroup(ServerGroup serverGroup)
{
Jedis jedis = _jedisPool.getResource();
Jedis jedis = _writePool.getResource();
try
{
@ -500,14 +502,14 @@ public class RedisServerRepository implements ServerRepository
catch (JedisConnectionException exception)
{
exception.printStackTrace();
_jedisPool.returnBrokenResource(jedis);
_writePool.returnBrokenResource(jedis);
jedis = null;
}
finally
{
if (jedis != null)
{
_jedisPool.returnResource(jedis);
_writePool.returnResource(jedis);
}
}
}
@ -516,7 +518,7 @@ public class RedisServerRepository implements ServerRepository
public ServerGroup getServerGroup(String serverGroup)
{
ServerGroup server = null;
Jedis jedis = _jedisPool.getResource();
Jedis jedis = _readPool.getResource();
try
{
String key = concatenate("servergroups", serverGroup);
@ -527,14 +529,14 @@ public class RedisServerRepository implements ServerRepository
catch (JedisConnectionException exception)
{
exception.printStackTrace();
_jedisPool.returnBrokenResource(jedis);
_readPool.returnBrokenResource(jedis);
jedis = null;
}
finally
{
if (jedis != null)
{
_jedisPool.returnResource(jedis);
_readPool.returnResource(jedis);
}
}

View File

@ -15,7 +15,8 @@ public class ServerCommandManager
public final String SERVER_COMMANDS_CHANNEL = "commands.server";
private JedisPool _jedisPool;
private JedisPool _writePool;
private JedisPool _readPool;
private Map<String, CommandType> _commandTypes;
private String _localServerName;
@ -27,8 +28,8 @@ public class ServerCommandManager
*/
private ServerCommandManager()
{
_jedisPool = new JedisPool(new JedisPoolConfig(), ServerManager.DEFAULT_REDIS_HOST,
ServerManager.DEFAULT_REDIS_PORT);
_writePool = Utility.getPool(true);
_readPool = Utility.getPool(false);
_commandTypes = new HashMap<String, CommandType>();
initialize();
@ -40,7 +41,7 @@ public class ServerCommandManager
*/
private void initialize()
{
final Jedis jedis = _jedisPool.getResource();
final Jedis jedis = _readPool.getResource();
// Spin up a new thread and subscribe to the Redis pubsub network
Thread thread = new Thread("Redis Manager")
@ -57,7 +58,7 @@ public class ServerCommandManager
}
finally
{
_jedisPool.returnResource(jedis);
_readPool.returnResource(jedis);
}
}
};
@ -75,7 +76,7 @@ public class ServerCommandManager
{
public void run()
{
Jedis jedis = _jedisPool.getResource();
Jedis jedis = _writePool.getResource();
try
{
@ -89,7 +90,7 @@ public class ServerCommandManager
}
finally
{
_jedisPool.returnResource(jedis);
_writePool.returnResource(jedis);
}
}
}).start();

View File

@ -10,11 +10,10 @@ import java.util.Map;
*/
public class ServerManager
{
// The host of the default redis database used for server repository
public final static String DEFAULT_REDIS_HOST = "10.33.53.16";
// The default port used by redis databases
public final static int DEFAULT_REDIS_PORT = 6379;
public final static ConnectionData DEFAULT_MASTER_CONNECTION = new ConnectionData("10.33.53.16", 6379);
public final static ConnectionData DEFAULT_SLAVE_CONNECTION = new ConnectionData("10.33.53.16", 6378);
// The cached repository instances
private static Map<Region, ServerRepository> repositories = new HashMap<Region, ServerRepository>();
@ -25,11 +24,11 @@ public class ServerManager
* @param region - the geographical region of the {@link ServerRepository}.
* @return a newly instanced (or cached) {@link ServerRepository} for the specified {@code region}.
*/
public static ServerRepository getServerRepository(String host, int port, Region region)
public static ServerRepository getServerRepository(ConnectionData writeConn, ConnectionData readConn, Region region)
{
if (repositories.containsKey(region)) return repositories.get(region);
ServerRepository repository = new RedisServerRepository(host, port, region);
ServerRepository repository = new RedisServerRepository(writeConn, readConn, region);
repositories.put(region, repository);
return repository;
}
@ -42,7 +41,7 @@ public class ServerManager
*/
public static ServerRepository getServerRepository(Region region)
{
return getServerRepository(DEFAULT_REDIS_HOST, DEFAULT_REDIS_PORT, region);
return getServerRepository(DEFAULT_MASTER_CONNECTION, DEFAULT_SLAVE_CONNECTION, region);
}
}

View File

@ -20,7 +20,8 @@ public class Utility
public static Gson getGson() { return _gson; }
// Public static jedis pool for interacting with central default jedis repo.
private static JedisPool _jedisPool;
private static JedisPool _masterPool;
private static JedisPool _slavePool;
/**
* @param object - the (non-null) object to serialize
@ -66,7 +67,8 @@ public class Utility
public static long currentTimeSeconds()
{
long currentTime = 0;
Jedis jedis = getPool().getResource();
JedisPool pool = getPool(false);
Jedis jedis = pool.getResource();
try
{
@ -74,7 +76,7 @@ public class Utility
}
finally
{
getPool().returnResource(jedis);
pool.returnResource(jedis);
}
return currentTime;
@ -87,7 +89,8 @@ public class Utility
public static long currentTimeMillis()
{
long currentTime = 0;
Jedis jedis = getPool().getResource();
JedisPool pool = getPool(false);
Jedis jedis = pool.getResource();
try
{
@ -95,20 +98,44 @@ public class Utility
}
finally
{
getPool().returnResource(jedis);
pool.returnResource(jedis);
}
return currentTime * 1000;
}
public static JedisPool getPool()
/**
* @param connData - the connection data specifying the database to be connected to.
* @return a newly instantiated {@link JedisPool} connected to the provided {@link ConnectionData} repository.
*/
public static JedisPool generatePool(ConnectionData connData)
{
if (_jedisPool == null)
return new JedisPool(new JedisPoolConfig(), connData.getHost(), connData.getPort());
}
/**
* @param writeable - whether or not the Jedis connections returned should be writeable to.
* @return a globally available {@link JedisPool}
*/
public static JedisPool getPool(boolean writeable)
{
if (writeable)
{
_jedisPool = new JedisPool(new JedisPoolConfig(),
ServerManager.DEFAULT_REDIS_HOST, ServerManager.DEFAULT_REDIS_PORT);
if (_masterPool == null)
{
_masterPool = generatePool(ServerManager.DEFAULT_MASTER_CONNECTION);
}
return _masterPool;
}
else
{
if (_slavePool == null)
{
_slavePool = generatePool(ServerManager.DEFAULT_SLAVE_CONNECTION);
}
return _slavePool;
}
return _jedisPool;
}
}