diff --git a/Makefile b/Makefile index 21eaa2b..e0189f2 100644 --- a/Makefile +++ b/Makefile @@ -61,6 +61,7 @@ save "" appendonly no endef +# SENTINELS define REDIS_SENTINEL1 port 26379 daemonize yes @@ -97,6 +98,40 @@ pidfile /tmp/sentinel3.pid logfile /tmp/sentinel3.log endef +# CLUSTER REDIS NODES +define REDIS_CLUSTER_NODE1_CONF +daemonize yes +port 7379 +pidfile /tmp/redis_cluster_node1.pid +logfile /tmp/redis_cluster_node1.log +save "" +appendonly no +cluster-enabled yes +cluster-config-file /tmp/redis_cluster_node1.conf +endef + +define REDIS_CLUSTER_NODE2_CONF +daemonize yes +port 7380 +pidfile /tmp/redis_cluster_node2.pid +logfile /tmp/redis_cluster_node2.log +save "" +appendonly no +cluster-enabled yes +cluster-config-file /tmp/redis_cluster_node2.conf +endef + +define REDIS_CLUSTER_NODE3_CONF +daemonize yes +port 7381 +pidfile /tmp/redis_cluster_node3.pid +logfile /tmp/redis_cluster_node3.log +save "" +appendonly no +cluster-enabled yes +cluster-config-file /tmp/redis_cluster_node3.conf +endef + export REDIS1_CONF export REDIS2_CONF export REDIS3_CONF @@ -106,6 +141,9 @@ export REDIS6_CONF export REDIS_SENTINEL1 export REDIS_SENTINEL2 export REDIS_SENTINEL3 +export REDIS_CLUSTER_NODE1_CONF +export REDIS_CLUSTER_NODE2_CONF +export REDIS_CLUSTER_NODE3_CONF start: echo "$$REDIS1_CONF" | redis-server - @@ -119,6 +157,9 @@ start: echo "$$REDIS_SENTINEL2" > /tmp/sentinel2.conf && redis-server /tmp/sentinel2.conf --sentinel @sleep 0.5 echo "$$REDIS_SENTINEL3" > /tmp/sentinel3.conf && redis-server /tmp/sentinel3.conf --sentinel + echo "$$REDIS_CLUSTER_NODE1_CONF" | redis-server - + echo "$$REDIS_CLUSTER_NODE2_CONF" | redis-server - + echo "$$REDIS_CLUSTER_NODE3_CONF" | redis-server - stop: kill `cat /tmp/redis1.pid` @@ -131,6 +172,12 @@ stop: kill `cat /tmp/sentinel1.pid` kill `cat /tmp/sentinel2.pid` kill `cat /tmp/sentinel3.pid` + kill `cat /tmp/redis_cluster_node1.pid` || true + kill `cat /tmp/redis_cluster_node2.pid` || true + kill `cat /tmp/redis_cluster_node3.pid` || true + rm -f /tmp/redis_cluster_node1.conf + rm -f /tmp/redis_cluster_node2.conf + rm -f /tmp/redis_cluster_node3.conf test: make start diff --git a/pom.xml b/pom.xml index 9409ec9..f6575eb 100644 --- a/pom.xml +++ b/pom.xml @@ -47,6 +47,7 @@ localhost:6379,localhost:6380,localhost:6381,localhost:6382,localhost:6383,localhost:6384 localhost:26379,localhost:26380,localhost:26381 + localhost:7379,localhost:7380,localhost:7381 github diff --git a/src/main/java/redis/clients/jedis/BinaryClient.java b/src/main/java/redis/clients/jedis/BinaryClient.java index acb120b..1b9db49 100644 --- a/src/main/java/redis/clients/jedis/BinaryClient.java +++ b/src/main/java/redis/clients/jedis/BinaryClient.java @@ -1100,4 +1100,9 @@ public class BinaryClient extends Connection { public void hincrByFloat(final byte[] key, final byte[] field, double increment) { sendCommand(HINCRBYFLOAT, key, field, toByteArray(increment)); } + + public void cluster(final byte[]... args) { + sendCommand(CLUSTER, args); + } + } diff --git a/src/main/java/redis/clients/jedis/Client.java b/src/main/java/redis/clients/jedis/Client.java index 9c97e38..b726821 100644 --- a/src/main/java/redis/clients/jedis/Client.java +++ b/src/main/java/redis/clients/jedis/Client.java @@ -807,4 +807,12 @@ public class Client extends BinaryClient implements Commands { public void hincrByFloat(final String key, final String field, double increment) { hincrByFloat(SafeEncoder.encode(key), SafeEncoder.encode(field), increment); } + + public void cluster(final String... args) { + final byte[][] arg = new byte[args.length][]; + for (int i = 0; i < arg.length; i++) { + arg[i] = SafeEncoder.encode(args[i]); + } + cluster(arg); + } } diff --git a/src/main/java/redis/clients/jedis/Jedis.java b/src/main/java/redis/clients/jedis/Jedis.java index 23cfd3d..d2daca5 100644 --- a/src/main/java/redis/clients/jedis/Jedis.java +++ b/src/main/java/redis/clients/jedis/Jedis.java @@ -3076,4 +3076,10 @@ public class Jedis extends BinaryJedis implements JedisCommands, MultiKeyCommand String relpy = client.getBulkReply(); return (relpy != null ? new Double(relpy) : null); } + + public String clusterNodes() { + checkIsInMulti(); + client.cluster(Protocol.CLUSTER_NODES); + return client.getBulkReply(); + } } diff --git a/src/main/java/redis/clients/jedis/Protocol.java b/src/main/java/redis/clients/jedis/Protocol.java index f85c324..e4a9126 100644 --- a/src/main/java/redis/clients/jedis/Protocol.java +++ b/src/main/java/redis/clients/jedis/Protocol.java @@ -30,6 +30,8 @@ public final class Protocol { public static final String SENTINEL_RESET = "reset"; public static final String SENTINEL_SLAVES = "slaves"; + public static final String CLUSTER_NODES = "nodes"; + private Protocol() { // this prevent the class from instantiation } @@ -155,7 +157,7 @@ public final class Protocol { public static enum Command { PING, SET, GET, QUIT, EXISTS, DEL, TYPE, FLUSHDB, KEYS, RANDOMKEY, RENAME, RENAMENX, RENAMEX, DBSIZE, EXPIRE, EXPIREAT, TTL, SELECT, MOVE, FLUSHALL, GETSET, MGET, SETNX, SETEX, MSET, MSETNX, DECRBY, DECR, INCRBY, INCR, APPEND, SUBSTR, HSET, HGET, HSETNX, HMSET, HMGET, HINCRBY, HEXISTS, HDEL, HLEN, HKEYS, HVALS, HGETALL, RPUSH, LPUSH, LLEN, LRANGE, LTRIM, LINDEX, LSET, LREM, LPOP, RPOP, RPOPLPUSH, SADD, SMEMBERS, SREM, SPOP, SMOVE, SCARD, SISMEMBER, SINTER, SINTERSTORE, SUNION, SUNIONSTORE, SDIFF, SDIFFSTORE, SRANDMEMBER, ZADD, ZRANGE, ZREM, ZINCRBY, ZRANK, ZREVRANK, ZREVRANGE, ZCARD, ZSCORE, MULTI, DISCARD, EXEC, WATCH, UNWATCH, SORT, BLPOP, BRPOP, AUTH, SUBSCRIBE, PUBLISH, UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE, ZCOUNT, ZRANGEBYSCORE, ZREVRANGEBYSCORE, ZREMRANGEBYRANK, ZREMRANGEBYSCORE, ZUNIONSTORE, ZINTERSTORE, SAVE, BGSAVE, BGREWRITEAOF, LASTSAVE, SHUTDOWN, INFO, MONITOR, SLAVEOF, CONFIG, STRLEN, SYNC, LPUSHX, PERSIST, RPUSHX, ECHO, LINSERT, DEBUG, BRPOPLPUSH, SETBIT, GETBIT, SETRANGE, GETRANGE, EVAL, EVALSHA, SCRIPT, SLOWLOG, OBJECT, BITCOUNT, BITOP, SENTINEL, - DUMP, RESTORE, PEXPIRE, PEXPIREAT, PTTL, INCRBYFLOAT, PSETEX, CLIENT, TIME, MIGRATE, HINCRBYFLOAT; + DUMP, RESTORE, PEXPIRE, PEXPIREAT, PTTL, INCRBYFLOAT, PSETEX, CLIENT, TIME, MIGRATE, HINCRBYFLOAT, CLUSTER; public final byte[] raw; diff --git a/src/test/java/redis/clients/jedis/tests/HostAndPortUtil.java b/src/test/java/redis/clients/jedis/tests/HostAndPortUtil.java index be64c53..c7be599 100644 --- a/src/test/java/redis/clients/jedis/tests/HostAndPortUtil.java +++ b/src/test/java/redis/clients/jedis/tests/HostAndPortUtil.java @@ -9,83 +9,106 @@ import redis.clients.jedis.Protocol; public class HostAndPortUtil { private static List redisHostAndPortList = new ArrayList(); private static List sentinelHostAndPortList = new ArrayList(); + private static List clusterHostAndPortList = new ArrayList(); static { - - HostAndPort defaulthnp1 = new HostAndPort("localhost", Protocol.DEFAULT_PORT); - redisHostAndPortList.add(defaulthnp1); - HostAndPort defaulthnp2 = new HostAndPort("localhost", Protocol.DEFAULT_PORT + 1); - redisHostAndPortList.add(defaulthnp2); - - HostAndPort defaulthnp3 = new HostAndPort("localhost", Protocol.DEFAULT_PORT + 2); - redisHostAndPortList.add(defaulthnp3); - - HostAndPort defaulthnp4 = new HostAndPort("localhost", Protocol.DEFAULT_PORT + 3); - redisHostAndPortList.add(defaulthnp4); - - HostAndPort defaulthnp5 = new HostAndPort("localhost", Protocol.DEFAULT_PORT + 4); - redisHostAndPortList.add(defaulthnp5); - - HostAndPort defaulthnp6 = new HostAndPort("localhost", Protocol.DEFAULT_PORT + 5); - redisHostAndPortList.add(defaulthnp6); - - HostAndPort defaulthnp7 = new HostAndPort("localhost", Protocol.DEFAULT_SENTINEL_PORT); - sentinelHostAndPortList.add(defaulthnp7); - - HostAndPort defaulthnp8 = new HostAndPort("localhost", Protocol.DEFAULT_SENTINEL_PORT + 1); - sentinelHostAndPortList.add(defaulthnp8); - - HostAndPort defaulthnp9 = new HostAndPort("localhost", Protocol.DEFAULT_SENTINEL_PORT + 2); - sentinelHostAndPortList.add(defaulthnp9); + HostAndPort defaulthnp1 = new HostAndPort("localhost", + Protocol.DEFAULT_PORT); + redisHostAndPortList.add(defaulthnp1); - String envRedisHosts = System.getProperty("redis-hosts"); - String envSentinelHosts = System.getProperty("sentinel-hosts"); - - redisHostAndPortList = parseHosts(envRedisHosts, redisHostAndPortList); - sentinelHostAndPortList = parseHosts(envSentinelHosts, sentinelHostAndPortList); + HostAndPort defaulthnp2 = new HostAndPort("localhost", + Protocol.DEFAULT_PORT + 1); + redisHostAndPortList.add(defaulthnp2); + + HostAndPort defaulthnp3 = new HostAndPort("localhost", + Protocol.DEFAULT_PORT + 2); + redisHostAndPortList.add(defaulthnp3); + + HostAndPort defaulthnp4 = new HostAndPort("localhost", + Protocol.DEFAULT_PORT + 3); + redisHostAndPortList.add(defaulthnp4); + + HostAndPort defaulthnp5 = new HostAndPort("localhost", + Protocol.DEFAULT_PORT + 4); + redisHostAndPortList.add(defaulthnp5); + + HostAndPort defaulthnp6 = new HostAndPort("localhost", + Protocol.DEFAULT_PORT + 5); + redisHostAndPortList.add(defaulthnp6); + + HostAndPort defaulthnp7 = new HostAndPort("localhost", + Protocol.DEFAULT_SENTINEL_PORT); + sentinelHostAndPortList.add(defaulthnp7); + + HostAndPort defaulthnp8 = new HostAndPort("localhost", + Protocol.DEFAULT_SENTINEL_PORT + 1); + sentinelHostAndPortList.add(defaulthnp8); + + HostAndPort defaulthnp9 = new HostAndPort("localhost", + Protocol.DEFAULT_SENTINEL_PORT + 2); + sentinelHostAndPortList.add(defaulthnp9); + + clusterHostAndPortList.add(new HostAndPort("localhost", 7379)); + clusterHostAndPortList.add(new HostAndPort("localhost", 7380)); + clusterHostAndPortList.add(new HostAndPort("localhost", 7381)); + + String envRedisHosts = System.getProperty("redis-hosts"); + String envSentinelHosts = System.getProperty("sentinel-hosts"); + String envClusterHosts = System.getProperty("cluster-hosts"); + + redisHostAndPortList = parseHosts(envRedisHosts, redisHostAndPortList); + sentinelHostAndPortList = parseHosts(envSentinelHosts, + sentinelHostAndPortList); + clusterHostAndPortList = parseHosts(envClusterHosts, + clusterHostAndPortList); } - public static List parseHosts(String envHosts, List existingHostsAndPorts) { - - if (null != envHosts && 0 < envHosts.length()) { - - String[] hostDefs = envHosts.split(","); - - if (null != hostDefs && 2 <= hostDefs.length) { - - List envHostsAndPorts = new ArrayList(hostDefs.length); - - for (String hostDef : hostDefs) { - - String[] hostAndPort = hostDef.split(":"); - - if (null != hostAndPort && 2 == hostAndPort.length) { - String host = hostAndPort[0]; - int port = Protocol.DEFAULT_PORT; - - try { - port = Integer.parseInt(hostAndPort[1]); - } catch (final NumberFormatException nfe) { - } - - envHostsAndPorts.add(new HostAndPort(host, port)); - } - } - - return envHostsAndPorts; - } - } - - return existingHostsAndPorts; + public static List parseHosts(String envHosts, + List existingHostsAndPorts) { + + if (null != envHosts && 0 < envHosts.length()) { + + String[] hostDefs = envHosts.split(","); + + if (null != hostDefs && 2 <= hostDefs.length) { + + List envHostsAndPorts = new ArrayList( + hostDefs.length); + + for (String hostDef : hostDefs) { + + String[] hostAndPort = hostDef.split(":"); + + if (null != hostAndPort && 2 == hostAndPort.length) { + String host = hostAndPort[0]; + int port = Protocol.DEFAULT_PORT; + + try { + port = Integer.parseInt(hostAndPort[1]); + } catch (final NumberFormatException nfe) { + } + + envHostsAndPorts.add(new HostAndPort(host, port)); + } + } + + return envHostsAndPorts; + } + } + + return existingHostsAndPorts; } - + public static List getRedisServers() { - return redisHostAndPortList; - } - - public static List getSentinelServers() { - return sentinelHostAndPortList; + return redisHostAndPortList; } + public static List getSentinelServers() { + return sentinelHostAndPortList; + } + + public static List getClusterServers() { + return clusterHostAndPortList; + } } diff --git a/src/test/java/redis/clients/jedis/tests/commands/ClusterCommandsTest.java b/src/test/java/redis/clients/jedis/tests/commands/ClusterCommandsTest.java new file mode 100644 index 0000000..1599b02 --- /dev/null +++ b/src/test/java/redis/clients/jedis/tests/commands/ClusterCommandsTest.java @@ -0,0 +1,41 @@ +package redis.clients.jedis.tests.commands; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import redis.clients.jedis.HostAndPort; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.tests.HostAndPortUtil; +import redis.clients.jedis.tests.JedisTestBase; + +public class ClusterCommandsTest extends JedisTestBase { + private Jedis node1; + private Jedis node2; + + @Before + public void setUp() throws Exception { + HostAndPort nodeInfo1 = HostAndPortUtil.getClusterServers().get(0); + HostAndPort nodeInfo2 = HostAndPortUtil.getClusterServers().get(1); + + node1 = new Jedis(nodeInfo1.getHost(), nodeInfo1.getPort()); + node1.connect(); + node1.flushAll(); + + node2 = new Jedis(nodeInfo2.getHost(), nodeInfo2.getPort()); + node2.connect(); + node2.flushAll(); + } + + @After + public void tearDown() { + node1.disconnect(); + node2.disconnect(); + } + + @Test + public void clusterNodes() { + String nodes = node1.clusterNodes(); + assertEquals(1, nodes.split("\n").length); + } +} \ No newline at end of file