Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.redisson.connection.MasterSlaveEntry Maven / Gradle / Ivy
/**
* Copyright 2018 Nikita Koksharov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.redisson.connection;
import java.net.InetSocketAddress;
import java.net.URI;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import org.redisson.api.NodeType;
import org.redisson.api.RFuture;
import org.redisson.client.RedisClient;
import org.redisson.client.RedisConnection;
import org.redisson.client.RedisPubSubConnection;
import org.redisson.client.protocol.CommandData;
import org.redisson.client.protocol.RedisCommand;
import org.redisson.cluster.ClusterConnectionManager;
import org.redisson.config.MasterSlaveServersConfig;
import org.redisson.config.ReadMode;
import org.redisson.config.SubscriptionMode;
import org.redisson.connection.ClientConnectionsEntry.FreezeReason;
import org.redisson.connection.balancer.LoadBalancerManager;
import org.redisson.connection.pool.MasterConnectionPool;
import org.redisson.connection.pool.MasterPubSubConnectionPool;
import org.redisson.misc.CountableListener;
import org.redisson.misc.RPromise;
import org.redisson.misc.RedissonPromise;
import org.redisson.misc.TransferListener;
import org.redisson.misc.URIBuilder;
import org.redisson.pubsub.PubSubConnectionEntry;
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;
/**
*
* @author Nikita Koksharov
*
*/
public class MasterSlaveEntry {
final Logger log = LoggerFactory.getLogger(getClass());
LoadBalancerManager slaveBalancer;
ClientConnectionsEntry masterEntry;
int references;
final MasterSlaveServersConfig config;
final ConnectionManager connectionManager;
final MasterConnectionPool writeConnectionPool;
final MasterPubSubConnectionPool pubSubConnectionPool;
final AtomicBoolean active = new AtomicBoolean(true);
String sslHostname;
public MasterSlaveEntry(ConnectionManager connectionManager, MasterSlaveServersConfig config) {
this.connectionManager = connectionManager;
this.config = config;
slaveBalancer = new LoadBalancerManager(config, connectionManager, this);
writeConnectionPool = new MasterConnectionPool(config, connectionManager, this);
pubSubConnectionPool = new MasterPubSubConnectionPool(config, connectionManager, this);
if (connectionManager instanceof ClusterConnectionManager) {
sslHostname = ((ClusterConnectionManager) connectionManager).getConfigEndpointHostName();
}
}
public MasterSlaveServersConfig getConfig() {
return config;
}
public List> initSlaveBalancer(Collection disconnectedNodes) {
boolean freezeMasterAsSlave = !config.getSlaveAddresses().isEmpty()
&& !config.checkSkipSlavesInit()
&& disconnectedNodes.size() < config.getSlaveAddresses().size();
List> result = new LinkedList>();
RFuture f = addSlave(config.getMasterAddress(), freezeMasterAsSlave, NodeType.MASTER);
result.add(f);
for (URI address : config.getSlaveAddresses()) {
f = addSlave(address, disconnectedNodes.contains(address), NodeType.SLAVE);
result.add(f);
}
return result;
}
public RFuture setupMasterEntry(InetSocketAddress address, URI uri) {
RedisClient client = connectionManager.createClient(NodeType.MASTER, address, uri, sslHostname);
return setupMasterEntry(client);
}
public RFuture setupMasterEntry(URI address) {
RedisClient client = connectionManager.createClient(NodeType.MASTER, address, sslHostname);
return setupMasterEntry(client);
}
private RFuture setupMasterEntry(final RedisClient client) {
final RPromise result = new RedissonPromise();
RFuture addrFuture = client.resolveAddr();
addrFuture.addListener(new FutureListener() {
@Override
public void operationComplete(Future future) throws Exception {
if (!future.isSuccess()) {
result.tryFailure(future.cause());
return;
}
masterEntry = new ClientConnectionsEntry(
client,
config.getMasterConnectionMinimumIdleSize(),
config.getMasterConnectionPoolSize(),
config.getSubscriptionConnectionMinimumIdleSize(),
config.getSubscriptionConnectionPoolSize(),
connectionManager,
NodeType.MASTER);
int counter = 1;
if (config.getSubscriptionMode() == SubscriptionMode.MASTER) {
counter++;
}
CountableListener listener = new CountableListener(result, client, counter);
RFuture writeFuture = writeConnectionPool.add(masterEntry);
writeFuture.addListener(listener);
if (config.getSubscriptionMode() == SubscriptionMode.MASTER) {
RFuture pubSubFuture = pubSubConnectionPool.add(masterEntry);
pubSubFuture.addListener(listener);
}
}
});
return result;
}
public boolean slaveDown(ClientConnectionsEntry entry, FreezeReason freezeReason) {
ClientConnectionsEntry e = slaveBalancer.freeze(entry, freezeReason);
if (e == null) {
return false;
}
return slaveDown(entry);
}
public boolean slaveDown(InetSocketAddress address, FreezeReason freezeReason) {
ClientConnectionsEntry entry = slaveBalancer.freeze(address, freezeReason);
if (entry == null) {
return false;
}
return slaveDown(entry);
}
public boolean slaveDown(URI address, FreezeReason freezeReason) {
ClientConnectionsEntry entry = slaveBalancer.freeze(address, freezeReason);
if (entry == null) {
return false;
}
return slaveDown(entry);
}
private boolean slaveDown(ClientConnectionsEntry entry) {
// add master as slave if no more slaves available
if (!config.checkSkipSlavesInit() && slaveBalancer.getAvailableClients() == 0) {
if (slaveBalancer.unfreeze(masterEntry.getClient().getAddr(), FreezeReason.SYSTEM)) {
log.info("master {} used as slave", masterEntry.getClient().getAddr());
}
}
entry.reset();
closeConnections(entry);
for (RedisPubSubConnection connection : entry.getAllSubscribeConnections()) {
connectionManager.getSubscribeService().reattachPubSub(connection);
}
entry.getAllSubscribeConnections().clear();
return true;
}
private void closeConnections(ClientConnectionsEntry entry) {
// 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();
}
}
private void reattachBlockingQueue(RedisConnection connection) {
final CommandData commandData = connection.getCurrentCommand();
if (commandData == null
|| !commandData.isBlockingCommand()
|| commandData.getPromise().isDone()) {
return;
}
RFuture newConnection = connectionWriteOp(commandData.getCommand());
newConnection.addListener(new FutureListener() {
@Override
public void operationComplete(Future future) throws Exception {
if (!future.isSuccess()) {
log.error("Can't resubscribe blocking queue {}", commandData);
return;
}
final RedisConnection newConnection = future.getNow();
final FutureListener listener = new FutureListener() {
@Override
public void operationComplete(Future future) throws Exception {
releaseWrite(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);
releaseWrite(newConnection);
log.error("Can't resubscribe blocking queue {}", commandData);
}
}
});
}
});
}
public boolean hasSlave(RedisClient redisClient) {
return slaveBalancer.contains(redisClient);
}
public boolean hasSlave(InetSocketAddress addr) {
return slaveBalancer.contains(addr);
}
public boolean hasSlave(URI addr) {
return slaveBalancer.contains(addr);
}
public int getAvailableClients() {
return slaveBalancer.getAvailableClients();
}
public RFuture addSlave(URI address) {
return addSlave(address, false, NodeType.SLAVE);
}
public RFuture addSlave(InetSocketAddress address, URI uri) {
return addSlave(address, uri, false, NodeType.SLAVE);
}
private RFuture addSlave(final RedisClient client, final boolean freezed, final NodeType nodeType) {
final RPromise result = new RedissonPromise();
RFuture addrFuture = client.resolveAddr();
addrFuture.addListener(new FutureListener() {
@Override
public void operationComplete(Future future) throws Exception {
if (!future.isSuccess()) {
result.tryFailure(future.cause());
return;
}
ClientConnectionsEntry entry = new ClientConnectionsEntry(client,
config.getSlaveConnectionMinimumIdleSize(),
config.getSlaveConnectionPoolSize(),
config.getSubscriptionConnectionMinimumIdleSize(),
config.getSubscriptionConnectionPoolSize(), connectionManager, nodeType);
if (freezed) {
synchronized (entry) {
entry.setFreezed(freezed);
entry.setFreezeReason(FreezeReason.SYSTEM);
}
}
RFuture addFuture = slaveBalancer.add(entry);
addFuture.addListener(new TransferListener(result));
}
});
return result;
}
private RFuture addSlave(InetSocketAddress address, URI uri, final boolean freezed, final NodeType nodeType) {
RedisClient client = connectionManager.createClient(NodeType.SLAVE, address, uri, sslHostname);
return addSlave(client, freezed, nodeType);
}
private RFuture addSlave(URI address, final boolean freezed, final NodeType nodeType) {
RedisClient client = connectionManager.createClient(nodeType, address, sslHostname);
return addSlave(client, freezed, nodeType);
}
public Collection getAllEntries() {
return slaveBalancer.getEntries();
}
public RedisClient getClient() {
return masterEntry.getClient();
}
public boolean slaveUp(ClientConnectionsEntry entry, FreezeReason freezeReason) {
if (!slaveBalancer.unfreeze(entry, freezeReason)) {
return false;
}
InetSocketAddress addr = masterEntry.getClient().getAddr();
// exclude master from slaves
if (!config.checkSkipSlavesInit()
&& !addr.equals(entry.getClient().getAddr())) {
if (slaveDown(addr, FreezeReason.SYSTEM)) {
log.info("master {} excluded from slaves", addr);
}
}
return true;
}
public boolean isSlaveUnfreezed(URI address) {
return slaveBalancer.isUnfreezed(address);
}
public boolean slaveUp(URI address, FreezeReason freezeReason) {
if (!slaveBalancer.unfreeze(address, freezeReason)) {
return false;
}
InetSocketAddress addr = masterEntry.getClient().getAddr();
// exclude master from slaves
if (!config.checkSkipSlavesInit()
&& !URIBuilder.compare(addr, address)) {
if (slaveDown(addr, FreezeReason.SYSTEM)) {
log.info("master {} excluded from slaves", addr);
}
}
return true;
}
public boolean slaveUp(InetSocketAddress address, FreezeReason freezeReason) {
if (!slaveBalancer.unfreeze(address, freezeReason)) {
return false;
}
InetSocketAddress addr = masterEntry.getClient().getAddr();
// exclude master from slaves
if (!config.checkSkipSlavesInit()
&& !addr.equals(address)) {
if (slaveDown(addr, FreezeReason.SYSTEM)) {
log.info("master {} excluded from slaves", addr);
}
}
return true;
}
/**
* Freeze slave with redis(s)://host:port
from slaves list.
* Re-attach pub/sub listeners from it to other slave.
* Shutdown old master client.
*
* @param address of Redis
* @return client
*/
public RFuture changeMaster(URI address) {
final ClientConnectionsEntry oldMaster = masterEntry;
RFuture future = setupMasterEntry(address);
changeMaster(address, oldMaster, future);
return future;
}
public void changeMaster(InetSocketAddress address, URI uri) {
final ClientConnectionsEntry oldMaster = masterEntry;
RFuture future = setupMasterEntry(address, uri);
changeMaster(uri, oldMaster, future);
}
private void changeMaster(final URI address, final ClientConnectionsEntry oldMaster,
RFuture future) {
future.addListener(new FutureListener() {
@Override
public void operationComplete(Future future) throws Exception {
if (!future.isSuccess()) {
log.error("Can't change master to: {}", address);
return;
}
RedisClient newMasterClient = future.getNow();
writeConnectionPool.remove(oldMaster);
pubSubConnectionPool.remove(oldMaster);
oldMaster.freezeMaster(FreezeReason.MANAGER);
slaveDown(oldMaster);
slaveBalancer.changeType(oldMaster.getClient().getAddr(), NodeType.SLAVE);
slaveBalancer.changeType(newMasterClient.getAddr(), NodeType.MASTER);
// more than one slave available, so master can be removed from slaves
if (!config.checkSkipSlavesInit()
&& slaveBalancer.getAvailableClients() > 1) {
slaveDown(newMasterClient.getAddr(), FreezeReason.SYSTEM);
}
oldMaster.getClient().shutdownAsync();
log.info("master {} has changed to {}", oldMaster.getClient().getAddr(), masterEntry.getClient().getAddr());
}
});
}
public RFuture shutdownAsync() {
if (!active.compareAndSet(true, false)) {
return RedissonPromise.newSucceededFuture(null);
}
RPromise result = new RedissonPromise();
CountableListener listener = new CountableListener(result, null, 2);
masterEntry.getClient().shutdownAsync().addListener(listener);
slaveBalancer.shutdownAsync().addListener(listener);
return result;
}
public RFuture connectionWriteOp(RedisCommand command) {
return writeConnectionPool.get(command);
}
public RFuture connectionReadOp(RedisCommand command) {
if (config.getReadMode() == ReadMode.MASTER) {
return connectionWriteOp(command);
}
return slaveBalancer.nextConnection(command);
}
public RFuture connectionReadOp(RedisCommand command, URI addr) {
return slaveBalancer.getConnection(command, addr);
}
public RFuture connectionReadOp(RedisCommand command, RedisClient client) {
if (config.getReadMode() == ReadMode.MASTER) {
return connectionWriteOp(command);
}
return slaveBalancer.getConnection(command, client);
}
public RFuture nextPubSubConnection() {
if (config.getSubscriptionMode() == SubscriptionMode.MASTER) {
return pubSubConnectionPool.get();
}
return slaveBalancer.nextPubSubConnection();
}
public void returnPubSubConnection(PubSubConnectionEntry entry) {
if (config.getSubscriptionMode() == SubscriptionMode.MASTER) {
pubSubConnectionPool.returnConnection(masterEntry, entry.getConnection());
return;
}
slaveBalancer.returnPubSubConnection(entry.getConnection());
}
public void releaseWrite(RedisConnection connection) {
writeConnectionPool.returnConnection(masterEntry, connection);
}
public void releaseRead(RedisConnection connection) {
if (config.getReadMode() == ReadMode.MASTER) {
releaseWrite(connection);
return;
}
slaveBalancer.returnConnection(connection);
}
public void incReference() {
references++;
}
public int decReference() {
return --references;
}
public int getReferences() {
return references;
}
@Override
public String toString() {
return "MasterSlaveEntry [masterEntry=" + masterEntry + "]";
}
}