├── .gitignore
├── README.md
├── pom.xml
└── src
├── main
└── java
│ └── com
│ └── mkfree
│ └── sentinel
│ ├── RedisInfo.java
│ ├── RedisSentinel.java
│ ├── RedisSentinelJedisPool.java
│ └── RedisSentinelShardedJedisPool.java
└── test
└── java
└── com
└── mkfree
└── sentinel
└── RedisSentinelClientTest.java
/.gitignore:
--------------------------------------------------------------------------------
1 | classes/
2 | lib/
3 | .git/
4 | .settings/
5 | .project
6 | .classpath
7 | target/
8 | cache/
9 | lucene/
10 | u/
11 | logs/
12 | kblog/
13 | bin/
14 | config.product
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | redis-sentinel-java
2 | ========
3 |
4 | 本项目主要是做一个redis-sentinel java客户端集群的自动切换方案,主要为了学习而玩。我相信有非常多的bug,请原谅。
5 |
6 | - 简单使用
7 | - 环境要求
8 | - redis sentinel的集群redis环境
9 |
10 | ```
11 | git clone git@github.com:oyhk/redis-sentinel-java.git
12 | ```
13 |
14 | ```
15 | public static void main(String[] args) throws InterruptedException {
16 | String host = "192.168.9.17";
17 | int port = 26379;
18 | String clusterName = "master1";
19 | RedisSentinel redisSentinelJedisPool = new RedisSentinelJedisPool(host, port, clusterName);
20 |
21 | Jedis jedis = null;
22 | try {
23 | jedis = (Jedis) redisSentinelJedisPool.getResource();
24 | jedis.set("key", "value");
25 | } catch (Exception e) {
26 | e.printStackTrace();
27 | } finally {
28 | client.returnBrokenResource(jedis);
29 | }
30 | }
31 |
32 | ```
33 |
34 |
35 |
36 | 自动切换日志信息
37 |
38 | ```
39 | {info-refresh=9917, port=6379, quorum=1, num-slaves=1, flags=master, last-ok-ping-reply=392, pending-commands=0, num-other-sentinels=0, name=master1, last-ping-reply=392, runid=0dec551778d0ce9857478e82521a3b7ed2984070, ip=192.168.9.19}
40 | [192.168.9.19, 6379]
41 | set1
42 | set2
43 | set3
44 | set4
45 | [192.168.9.19, 6379]
46 | set5
47 | redis.clients.jedis.exceptions.JedisConnectionException: java.net.ConnectException: 拒绝连接
48 | at redis.clients.jedis.Connection.connect(Connection.java:137)
49 | at redis.clients.jedis.BinaryClient.connect(BinaryClient.java:65)
50 | at redis.clients.jedis.Connection.sendCommand(Connection.java:82)
51 | at redis.clients.jedis.BinaryClient.set(BinaryClient.java:82)
52 | at redis.clients.jedis.Client.set(Client.java:23)
53 | at redis.clients.jedis.Jedis.set(Jedis.java:43)
54 | at redis.clients.jedis.ShardedJedis.set(ShardedJedis.java:31)
55 | at com.mkfree.sentinel.RedisSentinelClientTest$1.run(RedisSentinelClientTest.java:26)
56 | at java.lang.Thread.run(Thread.java:722)
57 | Caused by: java.net.ConnectException: 拒绝连接
58 | at java.net.PlainSocketImpl.socketConnect(Native Method)
59 | at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:339)
60 | at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:200)
61 | at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:182)
62 | at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:391)
63 | at java.net.Socket.connect(Socket.java:579)
64 | at redis.clients.jedis.Connection.connect(Connection.java:132)
65 | ... 8 more
66 | [192.168.9.19, 6379]
67 | [192.168.9.19, 6379]
68 | [192.168.9.19, 6379]
69 | [192.168.9.18, 6379]
70 | 主redis发生故障,自动切换...
71 | [192.168.9.18, 6379]
72 | [192.168.9.18, 6379]
73 | set1
74 | set2
75 | set3
76 | [192.168.9.18, 6379]
77 | set4
78 | ```
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 | com.mkfree
5 | redis-sentinel-java
6 | 0.0.1-SNAPSHOT
7 |
8 | 2.2.1
9 | 4.10
10 |
11 |
12 |
13 | redis.clients
14 | jedis
15 | ${redis-client.version}
16 |
17 |
18 | junit
19 | junit
20 | ${junit.version}
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/main/java/com/mkfree/sentinel/RedisInfo.java:
--------------------------------------------------------------------------------
1 | package com.mkfree.sentinel;
2 |
3 | /**
4 | * redis集群主从信息
5 | *
6 | * @author oyhk
7 | *
8 | * 2013-11-27 下午4:06:14
9 | */
10 | public class RedisInfo {
11 | private String host;
12 | private int port;
13 | private String name;
14 |
15 | public String getHost() {
16 | return host;
17 | }
18 |
19 | public void setHost(String host) {
20 | this.host = host;
21 | }
22 |
23 | public int getPort() {
24 | return port;
25 | }
26 |
27 | public void setPort(int port) {
28 | this.port = port;
29 | }
30 |
31 | public String getName() {
32 | return name;
33 | }
34 |
35 | public void setName(String name) {
36 | this.name = name;
37 | }
38 |
39 | }
--------------------------------------------------------------------------------
/src/main/java/com/mkfree/sentinel/RedisSentinel.java:
--------------------------------------------------------------------------------
1 | package com.mkfree.sentinel;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 | import java.util.Map;
6 |
7 | import org.apache.commons.pool.impl.GenericObjectPool;
8 |
9 | import redis.clients.jedis.Jedis;
10 | import redis.clients.jedis.JedisPool;
11 | import redis.clients.jedis.JedisShardInfo;
12 | import redis.clients.jedis.ShardedJedis;
13 | import redis.clients.jedis.ShardedJedisPool;
14 |
15 | /**
16 | * redis 集群自动切换客户端
17 | *
18 | * @author oyhk
19 | *
20 | * 2013-12-3 上午11:12:08
21 | */
22 | public abstract class RedisSentinel {
23 |
24 | // jedis 分片 共享连接池(目前使用静态变量解决)
25 | public ShardedJedisPool shardedJedisPool = null;
26 | // jedis 不分片 共享连接池(目前使用静态变量解决)
27 | public JedisPool jedisPool = null;
28 | // 连接池的配置 (如果不设置,使用默认值)
29 | protected GenericObjectPool.Config poolConfig = new GenericObjectPool.Config();
30 | // jedisSentinel 监控实例
31 | protected Jedis jedisSentinel = null;
32 |
33 | // 常用的静态变量
34 | public static final String IP = "ip";
35 | public static final String PORT = "port";
36 | public static final String NAME = "name";
37 |
38 | private String nextMaster = null;
39 | private String upMaster = null;
40 |
41 | /**
42 | * 创建多个集群共享的连接池
43 | */
44 | protected void createJedisPool(String... clusterName) {
45 | List redisInfo = jedisSentinel.sentinelGetMasterAddrByName(clusterName[0]);
46 | String host = redisInfo.get(0);
47 | int port = Integer.parseInt(redisInfo.get(1));
48 | jedisPool = new JedisPool(poolConfig, host, port);
49 | }
50 |
51 | /**
52 | * 检查redis sentinel 主/从redis服务是否正常, 当发生故障时,当redis sentinel监控自动切换 从redis 升级为主redis,需要重新初始化jedispool
53 | *
54 | * @param redisSentinel 那种监控的连接池(是否分片)
55 | * @param againCheckTime 单位(毫秒/millisecond)
56 | * @param clusterName 集群名
57 | */
58 | protected void checkRedisSentinelServer(final RedisSentinel redisSentinel, final int againCheckTime, final String... clusterName) {
59 | System.out.println("检查redis sentinel 主/从redis服务是否正常,任务开始...");
60 | Runnable checkRedisSentinelRunnable = new Runnable() {
61 | @Override
62 | public void run() {
63 | try {
64 | while (true) {
65 | jedisSentinel.ping();
66 | List masters = jedisSentinel.sentinelGetMasterAddrByName(clusterName[0]);
67 | String master = masters.toString();
68 | if (upMaster == null || upMaster.equals("")) {
69 | upMaster = master;
70 | }
71 | if (nextMaster == null || nextMaster.equals("") || !nextMaster.equals(master)) {
72 | nextMaster = master;
73 | }
74 | if (nextMaster.equals(upMaster)) {
75 | continue;
76 | }
77 | System.out.println("主redis发生故障,自动切换...");
78 | if (redisSentinel instanceof RedisSentinelJedisPool) {
79 | createJedisPool(clusterName);// 重新初始化jedispool
80 | System.out.println("重新初始化jedispool...");
81 | } else if (redisSentinel instanceof RedisSentinelShardedJedisPool) {
82 | // 暂时不考虑分片
83 | }
84 | upMaster = nextMaster;
85 | Thread.sleep(againCheckTime);
86 | }
87 | } catch (Exception e) {
88 | e.printStackTrace();
89 | System.out.println("redis sentinel 监控异常,请检查...");
90 | }
91 | }
92 | };
93 | new Thread(checkRedisSentinelRunnable).start();
94 | }
95 |
96 | /**
97 | * 获取客户端连接池
98 | *
99 | * @return
100 | */
101 | public abstract Object getResource();
102 |
103 | /**
104 | * 返回一个客户端连接到连接池
105 | *
106 | * @param resource
107 | */
108 | public abstract void returnBrokenResource(ShardedJedis resource);
109 |
110 | /**
111 | * 返回一个客户端连接到连接池
112 | *
113 | * @param resource
114 | */
115 | public abstract void returnBrokenResource(Jedis resource);
116 |
117 | public GenericObjectPool.Config getPoolConfig() {
118 | return poolConfig;
119 | }
120 |
121 | public void setPoolConfig(GenericObjectPool.Config poolConfig) {
122 | this.poolConfig = poolConfig;
123 | }
124 |
125 | }
126 |
--------------------------------------------------------------------------------
/src/main/java/com/mkfree/sentinel/RedisSentinelJedisPool.java:
--------------------------------------------------------------------------------
1 | package com.mkfree.sentinel;
2 |
3 | import redis.clients.jedis.Jedis;
4 | import redis.clients.jedis.ShardedJedis;
5 |
6 | public class RedisSentinelJedisPool extends RedisSentinel {
7 |
8 | /**
9 | * 创建redis集群客户端
10 | *
11 | * @param host ip地址
12 | * @param port 端口
13 | * @param clusterName 群集名
14 | */
15 | public RedisSentinelJedisPool(String host, int port, String... clusterName) {
16 | this.jedisSentinel = new Jedis(host, port);
17 | this.createJedisPool(clusterName);
18 | this.checkRedisSentinelServer(this, 5000, clusterName);
19 | }
20 |
21 | @Override
22 | public Object getResource() {
23 | return jedisPool.getResource();
24 | }
25 |
26 | @Override
27 | public void returnBrokenResource(ShardedJedis resource) {
28 |
29 | }
30 |
31 | @Override
32 | public void returnBrokenResource(Jedis resource) {
33 | jedisPool.returnBrokenResource(resource);
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/com/mkfree/sentinel/RedisSentinelShardedJedisPool.java:
--------------------------------------------------------------------------------
1 | package com.mkfree.sentinel;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 | import java.util.Map;
6 |
7 | import org.apache.commons.pool.impl.GenericObjectPool;
8 |
9 | import redis.clients.jedis.Jedis;
10 | import redis.clients.jedis.JedisShardInfo;
11 | import redis.clients.jedis.ShardedJedis;
12 | import redis.clients.jedis.ShardedJedisPool;
13 |
14 | /**
15 | * 创建redis集群客户端 (这是一个集群并分片的连接池)
16 | *
17 | * @author oyhk
18 | *
19 | * 2013-12-3 下午6:00:25
20 | */
21 | public class RedisSentinelShardedJedisPool extends RedisSentinel {
22 |
23 | /**
24 | * 创建redis集群客户端
25 | *
26 | * @param host ip地址
27 | * @param port 端口
28 | * @param clusterName 群集名
29 | */
30 | public RedisSentinelShardedJedisPool(String host, int port, String clusterName) {
31 | this.jedisSentinel = new Jedis(host, port);
32 | this.createShardedJedisPool(clusterName);
33 | this.checkRedisSentinelServer(this, 5000, clusterName);
34 | }
35 |
36 | /**
37 | * 创建多个集群共享的连接池
38 | */
39 | private void createShardedJedisPool(String clusterName) {
40 | List redisInfos = new ArrayList();
41 | List