|
|
|
@ -28,6 +28,9 @@ import org.redisson.ReadMode;
|
|
|
|
|
import org.redisson.client.RedisClient;
|
|
|
|
|
import org.redisson.client.RedisConnection;
|
|
|
|
|
import org.redisson.client.RedisPubSubConnection;
|
|
|
|
|
import org.redisson.client.RedisPubSubListener;
|
|
|
|
|
import org.redisson.client.codec.Codec;
|
|
|
|
|
import org.redisson.client.protocol.CommandData;
|
|
|
|
|
import org.redisson.cluster.ClusterSlotRange;
|
|
|
|
|
import org.redisson.connection.ClientConnectionsEntry.FreezeReason;
|
|
|
|
|
import org.redisson.connection.balancer.LoadBalancerManager;
|
|
|
|
@ -37,7 +40,10 @@ import org.redisson.core.NodeType;
|
|
|
|
|
import org.slf4j.Logger;
|
|
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
|
|
|
|
|
|
import io.netty.channel.ChannelFuture;
|
|
|
|
|
import io.netty.channel.ChannelFutureListener;
|
|
|
|
|
import io.netty.util.concurrent.Future;
|
|
|
|
|
import io.netty.util.concurrent.FutureListener;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
@ -91,7 +97,8 @@ public class MasterSlaveEntry {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boolean slaveDown(String host, int port, FreezeReason freezeReason) {
|
|
|
|
|
if (!slaveBalancer.freeze(host, port, freezeReason)) {
|
|
|
|
|
ClientConnectionsEntry entry = slaveBalancer.freeze(host, port, freezeReason);
|
|
|
|
|
if (entry == null) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -102,8 +109,154 @@ public class MasterSlaveEntry {
|
|
|
|
|
log.info("master {}:{} used as slave", addr.getHostName(), addr.getPort());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// close all connections
|
|
|
|
|
while (true) {
|
|
|
|
|
final RedisConnection connection = entry.pollConnection();
|
|
|
|
|
if (connection == null) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
connection.closeAsync().addListener(new ChannelFutureListener() {
|
|
|
|
|
@Override
|
|
|
|
|
public void operationComplete(ChannelFuture future) throws Exception {
|
|
|
|
|
reattachBlockingQueue(connection);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// close all pub/sub connections
|
|
|
|
|
while (true) {
|
|
|
|
|
RedisPubSubConnection connection = entry.pollSubscribeConnection();
|
|
|
|
|
if (connection == null) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
connection.closeAsync();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (RedisPubSubConnection connection : entry.getAllSubscribeConnections()) {
|
|
|
|
|
reattachPubSub(connection);
|
|
|
|
|
}
|
|
|
|
|
entry.getAllSubscribeConnections().clear();
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void reattachPubSub(RedisPubSubConnection redisPubSubConnection) {
|
|
|
|
|
for (String channelName : redisPubSubConnection.getChannels().keySet()) {
|
|
|
|
|
PubSubConnectionEntry pubSubEntry = connectionManager.getPubSubEntry(channelName);
|
|
|
|
|
|
|
|
|
|
synchronized (pubSubEntry) {
|
|
|
|
|
pubSubEntry.close();
|
|
|
|
|
|
|
|
|
|
Collection<RedisPubSubListener> listeners = pubSubEntry.getListeners(channelName);
|
|
|
|
|
reattachPubSubListeners(channelName, listeners);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (String channelName : redisPubSubConnection.getPatternChannels().keySet()) {
|
|
|
|
|
PubSubConnectionEntry pubSubEntry = connectionManager.getPubSubEntry(channelName);
|
|
|
|
|
|
|
|
|
|
synchronized (pubSubEntry) {
|
|
|
|
|
pubSubEntry.close();
|
|
|
|
|
|
|
|
|
|
Collection<RedisPubSubListener> listeners = pubSubEntry.getListeners(channelName);
|
|
|
|
|
reattachPatternPubSubListeners(channelName, listeners);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void reattachPubSubListeners(final String channelName, final Collection<RedisPubSubListener> listeners) {
|
|
|
|
|
Codec subscribeCodec = connectionManager.unsubscribe(channelName);
|
|
|
|
|
if (!listeners.isEmpty()) {
|
|
|
|
|
Future<PubSubConnectionEntry> future = connectionManager.subscribe(subscribeCodec, channelName, null);
|
|
|
|
|
future.addListener(new FutureListener<PubSubConnectionEntry>() {
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void operationComplete(Future<PubSubConnectionEntry> future)
|
|
|
|
|
throws Exception {
|
|
|
|
|
if (!future.isSuccess()) {
|
|
|
|
|
log.error("Can't resubscribe topic channel: " + channelName);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
PubSubConnectionEntry newEntry = future.getNow();
|
|
|
|
|
for (RedisPubSubListener redisPubSubListener : listeners) {
|
|
|
|
|
newEntry.addListener(channelName, redisPubSubListener);
|
|
|
|
|
}
|
|
|
|
|
log.debug("resubscribed listeners for '{}' channel", channelName);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void reattachPatternPubSubListeners(final String channelName,
|
|
|
|
|
final Collection<RedisPubSubListener> listeners) {
|
|
|
|
|
Codec subscribeCodec = connectionManager.punsubscribe(channelName);
|
|
|
|
|
if (!listeners.isEmpty()) {
|
|
|
|
|
Future<PubSubConnectionEntry> future = connectionManager.psubscribe(channelName, subscribeCodec);
|
|
|
|
|
future.addListener(new FutureListener<PubSubConnectionEntry>() {
|
|
|
|
|
@Override
|
|
|
|
|
public void operationComplete(Future<PubSubConnectionEntry> future)
|
|
|
|
|
throws Exception {
|
|
|
|
|
if (!future.isSuccess()) {
|
|
|
|
|
log.error("Can't resubscribe topic channel: " + channelName);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PubSubConnectionEntry newEntry = future.getNow();
|
|
|
|
|
for (RedisPubSubListener redisPubSubListener : listeners) {
|
|
|
|
|
newEntry.addListener(channelName, redisPubSubListener);
|
|
|
|
|
}
|
|
|
|
|
log.debug("resubscribed listeners for '{}' channel-pattern", channelName);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void reattachBlockingQueue(RedisConnection connection) {
|
|
|
|
|
final CommandData<?, ?> commandData = connection.getCurrentCommand();
|
|
|
|
|
|
|
|
|
|
if (commandData == null
|
|
|
|
|
|| !commandData.isBlockingCommand()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Future<RedisConnection> newConnection = connectionReadOp();
|
|
|
|
|
newConnection.addListener(new FutureListener<RedisConnection>() {
|
|
|
|
|
@Override
|
|
|
|
|
public void operationComplete(Future<RedisConnection> future) throws Exception {
|
|
|
|
|
if (!future.isSuccess()) {
|
|
|
|
|
log.error("Can't resubscribe blocking queue {}", commandData);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
final RedisConnection newConnection = future.getNow();
|
|
|
|
|
|
|
|
|
|
final FutureListener<Object> listener = new FutureListener<Object>() {
|
|
|
|
|
@Override
|
|
|
|
|
public void operationComplete(Future<Object> future) throws Exception {
|
|
|
|
|
releaseRead(newConnection);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
commandData.getPromise().addListener(listener);
|
|
|
|
|
if (commandData.getPromise().isDone()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
ChannelFuture channelFuture = newConnection.send(commandData);
|
|
|
|
|
channelFuture.addListener(new ChannelFutureListener() {
|
|
|
|
|
@Override
|
|
|
|
|
public void operationComplete(ChannelFuture future) throws Exception {
|
|
|
|
|
if (!future.isSuccess()) {
|
|
|
|
|
listener.operationComplete(null);
|
|
|
|
|
commandData.getPromise().removeListener(listener);
|
|
|
|
|
releaseRead(newConnection);
|
|
|
|
|
log.error("Can't resubscribe blocking queue {}", commandData);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Future<Void> addSlave(String host, int port) {
|
|
|
|
|
return addSlave(host, port, true, NodeType.SLAVE);
|
|
|
|
|