Skip to content

Commit fd361a1

Browse files
christophstroblThomas Darimont
authored andcommitted
DATAREDIS-330 - Add support for SENTINEL commands.
add support for commands: SENTINEL FAILOVER master SENTINEL SLAVES master SENTINEL REMOVE name SENTINEL MONITOR name ip port quorum Original pull request: spring-projects#92.
1 parent 55ff415 commit fd361a1

29 files changed

+2073
-50
lines changed

docs/src/reference/docbook/reference/redis.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,8 @@ public RedisConnectionFactory jedisConnectionFactory() {
222222
return new JedisConnectionFactory(sentinelConfig);
223223
}
224224
]]></programlisting>
225+
<para>Sometimes direct interaction with the one of the Sentinels is required. Using <classname>RedisConnectionFactory.getSentinelConnection()</classname> or
226+
<classname>RedisConnection.getSentinelCommands()</classname> gives you access to the first active Sentinel configured.</para>
225227
</section>
226228

227229
<section id="redis:template">
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/*
2+
* Copyright 2014 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.redis.connection;
17+
18+
import java.io.IOException;
19+
import java.util.concurrent.ConcurrentHashMap;
20+
21+
import org.springframework.dao.DataAccessException;
22+
import org.springframework.dao.InvalidDataAccessApiUsageException;
23+
import org.springframework.dao.InvalidDataAccessResourceUsageException;
24+
import org.springframework.data.redis.RedisSystemException;
25+
26+
/**
27+
* @author Christoph Strobl
28+
* @since 1.4
29+
*/
30+
public abstract class AbstractRedisConnection implements RedisConnection {
31+
32+
private RedisSentinelConfiguration sentinelConfiguration;
33+
private ConcurrentHashMap<RedisNode, RedisSentinelConnection> connectionCache = new ConcurrentHashMap<RedisNode, RedisSentinelConnection>();
34+
35+
/*
36+
* (non-Javadoc)
37+
* @see org.springframework.data.redis.connection.RedisConnection#getSentinelCommands()
38+
*/
39+
@Override
40+
public RedisSentinelConnection getSentinelConnection() {
41+
42+
if (!hasRedisSentinelConfigured()) {
43+
throw new InvalidDataAccessResourceUsageException("No sentinels configured.");
44+
}
45+
46+
RedisNode node = selectActiveSentinel();
47+
RedisSentinelConnection connection = connectionCache.get(node);
48+
if (connection == null || !connection.isOpen()) {
49+
connection = getSentinelConnection(node);
50+
connectionCache.putIfAbsent(node, connection);
51+
}
52+
return connection;
53+
}
54+
55+
public void setSentinelConfiguration(RedisSentinelConfiguration sentinelConfiguration) {
56+
this.sentinelConfiguration = sentinelConfiguration;
57+
}
58+
59+
public boolean hasRedisSentinelConfigured() {
60+
return this.sentinelConfiguration != null;
61+
}
62+
63+
private RedisNode selectActiveSentinel() {
64+
65+
for (RedisNode node : this.sentinelConfiguration.getSentinels()) {
66+
if (isActive(node)) {
67+
return node;
68+
}
69+
}
70+
71+
throw new InvalidDataAccessApiUsageException("Could not find any active sentinels");
72+
}
73+
74+
/**
75+
* Check if node is active by sending ping.
76+
*
77+
* @param node
78+
* @return
79+
*/
80+
protected boolean isActive(RedisNode node) {
81+
return false;
82+
}
83+
84+
/**
85+
* Get {@link RedisSentinelCommands} connected to given node.
86+
*
87+
* @param sentinel
88+
* @return
89+
*/
90+
protected RedisSentinelConnection getSentinelConnection(RedisNode sentinel) {
91+
throw new UnsupportedOperationException("Sentinel is not supported by this client.");
92+
}
93+
94+
/*
95+
* (non-Javadoc)
96+
* @see org.springframework.data.redis.connection.RedisConnection#close()
97+
*/
98+
@Override
99+
public void close() throws DataAccessException {
100+
101+
if (!connectionCache.isEmpty()) {
102+
for (RedisNode node : connectionCache.keySet()) {
103+
RedisSentinelConnection connection = connectionCache.remove(node);
104+
if (connection.isOpen()) {
105+
try {
106+
connection.close();
107+
} catch (IOException e) {
108+
throw new RedisSystemException("Failed to close sentinel connection", e);
109+
}
110+
}
111+
}
112+
}
113+
}
114+
115+
}

src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2380,4 +2380,9 @@ public Cursor<StringTuple> zScan(String key, ScanOptions options) {
23802380
new TupleConverter());
23812381
}
23822382

2383+
@Override
2384+
public RedisSentinelConnection getSentinelConnection() {
2385+
return delegate.getSentinelConnection();
2386+
}
2387+
23832388
}

src/main/java/org/springframework/data/redis/connection/RedisConnection.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,10 @@ public interface RedisConnection extends RedisCommands {
9292
* @return the result of the executed commands.
9393
*/
9494
List<Object> closePipeline() throws RedisPipelineException;
95+
96+
/**
97+
* @return
98+
* @since 1.4
99+
*/
100+
RedisSentinelConnection getSentinelConnection();
95101
}

src/main/java/org/springframework/data/redis/connection/RedisConnectionFactory.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2011-2013 the original author or authors.
2+
* Copyright 2011-2014 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@
2222
* Thread-safe factory of Redis connections.
2323
*
2424
* @author Costin Leau
25+
* @author Christoph Strobl
2526
*/
2627
public interface RedisConnectionFactory extends PersistenceExceptionTranslator {
2728

@@ -41,4 +42,10 @@ public interface RedisConnectionFactory extends PersistenceExceptionTranslator {
4142
* @return Whether or not to convert pipeline and tx results
4243
*/
4344
boolean getConvertPipelineAndTxResults();
45+
46+
/**
47+
* @return
48+
* @since 1.4
49+
*/
50+
RedisSentinelConnection getSentinelConnection();
4451
}

src/main/java/org/springframework/data/redis/connection/RedisNode.java

Lines changed: 64 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,37 @@
1515
*/
1616
package org.springframework.data.redis.connection;
1717

18+
import org.springframework.util.Assert;
1819
import org.springframework.util.ObjectUtils;
1920

2021
/**
2122
* @author Christoph Strobl
23+
* @author Thomas Darimont
24+
*
2225
* @since 1.4
2326
*/
24-
public class RedisNode {
25-
26-
private final String host;
27-
private final int port;
28-
27+
public class RedisNode implements NamedNode {
28+
29+
private String name;
30+
private String host;
31+
private int port;
32+
33+
/**
34+
* Creates a new {@link RedisNode} with the given {@code host}, {@code port}.
35+
*
36+
* @param host must not be {@literal null}
37+
* @param port
38+
*/
2939
public RedisNode(String host, int port) {
40+
41+
Assert.notNull(host,"host must not be null!");
42+
3043
this.host = host;
3144
this.port = port;
3245
}
3346

47+
protected RedisNode() {}
48+
3449
public String getHost() {
3550
return host;
3651
}
@@ -43,6 +58,15 @@ public String asString() {
4358
return host + ":" + port;
4459
}
4560

61+
@Override
62+
public String getName() {
63+
return this.name;
64+
}
65+
66+
public void setName(String name) {
67+
this.name = name;
68+
}
69+
4670
@Override
4771
public String toString() {
4872
return asString();
@@ -77,7 +101,42 @@ public boolean equals(Object obj) {
77101
return false;
78102
}
79103

104+
if (!ObjectUtils.nullSafeEquals(this.name, other.name)) {
105+
return false;
106+
}
107+
80108
return true;
81109
}
82110

111+
/**
112+
* @author Christoph Strobl
113+
*
114+
* @since 1.4
115+
*/
116+
public static class RedisNodeBuilder {
117+
118+
private RedisNode node;
119+
120+
public RedisNodeBuilder() {
121+
node = new RedisNode();
122+
}
123+
124+
public RedisNodeBuilder withName(String name) {
125+
node.name = name;
126+
return this;
127+
}
128+
129+
public RedisNodeBuilder listeningAt(String host, int port) {
130+
131+
Assert.hasText(host, "Hostname must not be empty or null.");
132+
node.host = host;
133+
node.port = port;
134+
return this;
135+
}
136+
137+
public RedisNode build() {
138+
return this.node;
139+
}
140+
}
141+
83142
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright 2014 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.redis.connection;
17+
18+
import java.util.Collection;
19+
20+
/**
21+
* @author Christoph Strobl
22+
* @since 1.4
23+
*/
24+
public interface RedisSentinelCommands {
25+
26+
/**
27+
* Force a failover as if the {@literal master} was not reachable.
28+
*
29+
* @param master must not be {@literal null}.
30+
*/
31+
void failover(NamedNode master);
32+
33+
/**
34+
* Get a {@link Collection} of monitored masters and their state.
35+
*
36+
* @return Collection of {@link RedisServer}s. Never {@literal null}.
37+
*/
38+
Collection<RedisServer> masters();
39+
40+
/**
41+
* Show list of slaves for given {@literal master}.
42+
*
43+
* @param master must not be {@literal null}.
44+
* @return Collection of {@link RedisServer}s. Never {@literal null}.
45+
*/
46+
Collection<RedisServer> slaves(NamedNode master);
47+
48+
/**
49+
* Removes given {@literal master}. The server will no longer be monitored and will no longer be returned by
50+
* {@link #masters()}.
51+
*
52+
* @param master must not be {@literal null}.
53+
*/
54+
void remove(NamedNode master);
55+
56+
/**
57+
* Tell sentinel to start monitoring a new {@literal master} with the specified {@link RedisServer#getName()},
58+
* {@link RedisServer#getHost()}, {@link RedisServer#getPort()}, and {@link RedisServer#getQuorum()}.
59+
*
60+
* @param master must not be {@literal null}.
61+
*/
62+
void monitor(RedisServer master);
63+
64+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright 2014 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.redis.connection;
17+
18+
import java.io.Closeable;
19+
20+
/**
21+
* @author Christoph Strobl
22+
* @since 1.4
23+
*/
24+
public interface RedisSentinelConnection extends RedisSentinelCommands, Closeable {
25+
26+
/**
27+
* @return true if connected to server
28+
*/
29+
boolean isOpen();
30+
31+
}

0 commit comments

Comments
 (0)