diff --git a/src/main/java/redis/clients/jedis/BinaryClient.java b/src/main/java/redis/clients/jedis/BinaryClient.java index a5323d2..8f29407 100644 --- a/src/main/java/redis/clients/jedis/BinaryClient.java +++ b/src/main/java/redis/clients/jedis/BinaryClient.java @@ -807,4 +807,9 @@ public class BinaryClient extends Connection { public void objectEncoding(byte[] key) { sendCommand(OBJECT, ENCODING.raw, key); } + + public void sentinel(final byte[]... args) { + sendCommand(SENTINEL, args); + } + } diff --git a/src/main/java/redis/clients/jedis/Client.java b/src/main/java/redis/clients/jedis/Client.java index 39aae12..bfabe2d 100644 --- a/src/main/java/redis/clients/jedis/Client.java +++ b/src/main/java/redis/clients/jedis/Client.java @@ -703,4 +703,18 @@ public class Client extends BinaryClient implements Commands { public void objectEncoding(String key) { objectEncoding(SafeEncoder.encode(key)); } + + + public void sentinel(final String... args) { + final byte[][] arg = new byte[args.length][]; + for (int i = 0; i < arg.length; i++) { + arg[i] = SafeEncoder.encode(args[i]); + } + sentinel(arg); + } + + public void sentinel(final String cmd, String arg1, int arg2) { + sentinel(SafeEncoder.encode(cmd), SafeEncoder.encode(arg1), toByteArray(arg2)); + } + } diff --git a/src/main/java/redis/clients/jedis/Jedis.java b/src/main/java/redis/clients/jedis/Jedis.java index f27c2bc..58925ea 100644 --- a/src/main/java/redis/clients/jedis/Jedis.java +++ b/src/main/java/redis/clients/jedis/Jedis.java @@ -2,6 +2,7 @@ package redis.clients.jedis; import java.net.URI; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; @@ -2873,4 +2874,142 @@ public class Jedis extends BinaryJedis implements JedisCommands { client.objectIdletime(string); return client.getIntegerReply(); } + + /** + *
+   * redis 127.0.0.1:26381> sentinel masters
+   * 1)  1) "name"
+   *     2) "mymaster"
+   *     3) "ip"
+   *     4) "127.0.0.1"
+   *     5) "port"
+   *     6) "6379"
+   *     7) "runid"
+   *     8) "93d4d4e6e9c06d0eea36e27f31924ac26576081d"
+   *     9) "flags"
+   *    10) "master"
+   *    11) "pending-commands"
+   *    12) "0"
+   *    13) "last-ok-ping-reply"
+   *    14) "423"
+   *    15) "last-ping-reply"
+   *    16) "423"
+   *    17) "info-refresh"
+   *    18) "6107"
+   *    19) "num-slaves"
+   *    20) "1"
+   *    21) "num-other-sentinels"
+   *    22) "2"
+   *    23) "quorum"
+   *    24) "2"
+   *
+   * 
+ * + * @return + */ + public List> sentinelMasters() { + client.sentinel(Protocol.SENTINEL_MASTERS); + final List reply = client.getObjectMultiBulkReply(); + + final List> masters = new ArrayList>(); + for (Object obj : reply) { + masters.add(BuilderFactory.STRING_MAP.build((List) obj)); + } + return masters; + } + + + /** + *
+   * redis 127.0.0.1:26381> sentinel get-master-addr-by-name mymaster
+   * 1) "127.0.0.1"
+   * 2) "6379"
+   * 
+ * + * @param masterName + * @return two elements list of strings : host and port. + */ + public List sentinelGetMasterAddrByName(String masterName) { + client.sentinel(Protocol.SENTINEL_GET_MASTER_ADDR_BY_NAME, masterName); + final List reply = client.getObjectMultiBulkReply(); + return BuilderFactory.STRING_LIST.build(reply); + } + + /** + *
+   * redis 127.0.0.1:26381> sentinel reset mymaster
+   * (integer) 1
+   * 
+ * + * @param pattern + * @return + */ + public Long sentinelReset(String pattern) { + client.sentinel(Protocol.SENTINEL_RESET, pattern); + return client.getIntegerReply(); + } + + /** + *
+   * redis 127.0.0.1:26381> sentinel slaves mymaster
+   * 1)  1) "name"
+   *     2) "127.0.0.1:6380"
+   *     3) "ip"
+   *     4) "127.0.0.1"
+   *     5) "port"
+   *     6) "6380"
+   *     7) "runid"
+   *     8) "d7f6c0ca7572df9d2f33713df0dbf8c72da7c039"
+   *     9) "flags"
+   *    10) "slave"
+   *    11) "pending-commands"
+   *    12) "0"
+   *    13) "last-ok-ping-reply"
+   *    14) "47"
+   *    15) "last-ping-reply"
+   *    16) "47"
+   *    17) "info-refresh"
+   *    18) "657"
+   *    19) "master-link-down-time"
+   *    20) "0"
+   *    21) "master-link-status"
+   *    22) "ok"
+   *    23) "master-host"
+   *    24) "localhost"
+   *    25) "master-port"
+   *    26) "6379"
+   *    27) "slave-priority"
+   *    28) "100"
+   * 
+ * + * @param masterName + * @return + */ + public List> sentinelSlaves(String masterName) { + client.sentinel(Protocol.SENTINEL_SLAVES, masterName); + final List reply = client.getObjectMultiBulkReply(); + + final List> slaves = new ArrayList>(); + for (Object obj : reply) { + slaves.add(BuilderFactory.STRING_MAP.build((List) obj)); + } + return slaves; + } + + /** + *
+   * redis 127.0.0.1:26381> SENTINEL is-master-down-by-addr 127.0.0.1 1
+   * 1) (integer) 0
+   * 2) "?"
+   * redis 127.0.0.1:26381> SENTINEL is-master-down-by-addr 127.0.0.1 6379
+   * 1) (integer) 0
+   * 2) "aaef11fbb2712346a386078c7f9834e72ed51e96"
+   * 
+ * @return Long followed by the String (runid) + */ + public List sentinelIsMasterDownByAddr(String host, int port) { + client.sentinel(Protocol.SENTINEL_IS_MASTER_DOWN_BY_ADDR, host, port); + final List reply = client.getObjectMultiBulkReply(); + return Arrays.asList(BuilderFactory.LONG.build(reply.get(0)), BuilderFactory.STRING.build(reply.get(1))); + } } diff --git a/src/main/java/redis/clients/jedis/Protocol.java b/src/main/java/redis/clients/jedis/Protocol.java index b1e391d..6e1d364 100644 --- a/src/main/java/redis/clients/jedis/Protocol.java +++ b/src/main/java/redis/clients/jedis/Protocol.java @@ -24,6 +24,12 @@ public final class Protocol { public static final byte MINUS_BYTE = '-'; public static final byte COLON_BYTE = ':'; + public static final String SENTINEL_MASTERS = "masters"; + public static final String SENTINEL_GET_MASTER_ADDR_BY_NAME = "get-master-addr-by-name"; + public static final String SENTINEL_RESET = "reset"; + public static final String SENTINEL_SLAVES = "slaves"; + public static final String SENTINEL_IS_MASTER_DOWN_BY_ADDR = "is-master-down-by-addr"; + private Protocol() { // this prevent the class from instantiation } @@ -144,7 +150,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; + 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, SENTINEL; public final byte[] raw; diff --git a/src/test/java/redis/clients/jedis/tests/JedisSentinelTest.java b/src/test/java/redis/clients/jedis/tests/JedisSentinelTest.java new file mode 100644 index 0000000..6ca62b9 --- /dev/null +++ b/src/test/java/redis/clients/jedis/tests/JedisSentinelTest.java @@ -0,0 +1,47 @@ +package redis.clients.jedis.tests; + +import static junit.framework.Assert.*; + +import java.util.List; +import java.util.Map; + +import org.junit.Test; + +import redis.clients.jedis.Jedis; + +public class JedisSentinelTest { + + private static final String MASTER_NAME = "mymaster"; + + /** + * Based on redis/master/slave/sentinel configs from + * https://github.com/noise/redis-sentinel-tests. + */ + @Test + public void sentinel() { + Jedis j = new Jedis("localhost", 26379); + List> masters = j.sentinelMasters(); + final String masterName = masters.get(0).get("name"); + + assertEquals(MASTER_NAME, masterName); + + List masterHostAndPort = j.sentinelGetMasterAddrByName(masterName); + assertEquals("127.0.0.1", masterHostAndPort.get(0)); + assertEquals("6379", masterHostAndPort.get(1)); + + List> slaves = j.sentinelSlaves(masterName); + assertEquals("6379", slaves.get(0).get("master-port")); + + List isMasterDownByAddr = j.sentinelIsMasterDownByAddr("127.0.0.1", 6379); + assertEquals(Long.valueOf(0), (Long) isMasterDownByAddr.get(0)); + assertFalse("?".equals(isMasterDownByAddr.get(1))); + + isMasterDownByAddr = j.sentinelIsMasterDownByAddr("127.0.0.1", 1); + assertEquals(Long.valueOf(0), (Long) isMasterDownByAddr.get(0)); + assertTrue("?".equals(isMasterDownByAddr.get(1))); + + // DO NOT RE-RUN TEST TOO FAST, RESET TAKES SOME TIME TO... RESET + assertEquals(Long.valueOf(1), j.sentinelReset(masterName)); + assertEquals(Long.valueOf(0), j.sentinelReset("woof" + masterName)); + } +}