All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.lambdaworks.redis.masterslave.MasterSlaveTopologyRefresh Maven / Gradle / Ivy

Go to download

Advanced and thread-safe Java Redis client for synchronous, asynchronous, and reactive usage. Supports Cluster, Sentinel, Pipelining, Auto-Reconnect, Codecs and much more.

There is a newer version: 5.0.0.Beta1
Show newest version
/*
 * Copyright 2011-2016 the original author or authors.
 *
 * 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 com.lambdaworks.redis.masterslave;

import static com.lambdaworks.redis.masterslave.MasterSlaveUtils.findNodeByUri;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

import com.lambdaworks.redis.RedisClient;
import com.lambdaworks.redis.RedisCommandInterruptedException;
import com.lambdaworks.redis.RedisConnectionException;
import com.lambdaworks.redis.RedisURI;
import com.lambdaworks.redis.api.StatefulRedisConnection;
import com.lambdaworks.redis.cluster.models.partitions.Partitions;
import com.lambdaworks.redis.codec.StringCodec;
import com.lambdaworks.redis.models.role.RedisNodeDescription;

import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;

/**
 * Utility to refresh the Master-Slave topology view based on {@link RedisNodeDescription}.
 * 
 * @author Mark Paluch
 */
class MasterSlaveTopologyRefresh {

    private static final InternalLogger logger = InternalLoggerFactory.getInstance(MasterSlaveTopologyRefresh.class);
    private static final StringCodec CODEC = StringCodec.UTF8;

    private final NodeConnectionFactory nodeConnectionFactory;
    private final TopologyProvider topologyProvider;

    public MasterSlaveTopologyRefresh(RedisClient client, TopologyProvider topologyProvider) {

        this.nodeConnectionFactory = new ReflectiveNodeConnectionFactory(client);
        this.topologyProvider = topologyProvider;
    }

    /**
     * Load master slave nodes. Result contains an ordered list of {@link RedisNodeDescription}s. The sort key is the latency.
     * Nodes with lower latency come first.
     * 
     * @param seed collection of {@link RedisURI}s
     * @return mapping between {@link RedisURI} and {@link Partitions}
     */
    public List getNodes(RedisURI seed) {

        List nodes = topologyProvider.getNodes();

        addPasswordIfNeeded(nodes, seed);

        AsyncConnections asyncConnections = getConnections(nodes);
        Connections connections = null;

        try {

            connections = asyncConnections.get(seed.getTimeout(), seed.getUnit());
            Requests requestedPing = connections.requestPing();
            return getNodeSpecificViews(requestedPing, nodes, seed);

        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RedisCommandInterruptedException(e);
        } finally {
            if (connections != null) {
                connections.close();
            }
        }
    }

    private void addPasswordIfNeeded(List nodes, RedisURI seed) {

        if (seed.getPassword() != null && seed.getPassword().length != 0) {
            for (RedisNodeDescription node : nodes) {
                node.getUri().setPassword(new String(seed.getPassword()));
            }
        }
    }

    private List getNodeSpecificViews(Requests requestedPing, List nodes,
            RedisURI seed) throws InterruptedException {

        List result = new ArrayList<>();

        long timeout = seed.getUnit().toNanos(seed.getTimeout());
        Map latencies = new HashMap<>();

        requestedPing.await(timeout, TimeUnit.NANOSECONDS);

        for (RedisNodeDescription node : nodes) {

            TimedAsyncCommand future = requestedPing.getRequest(node.getUri());

            if (!future.isDone()) {
                continue;
            }

            RedisNodeDescription redisNodeDescription = findNodeByUri(nodes, node.getUri());
            latencies.put(redisNodeDescription, future.duration());
            result.add(redisNodeDescription);
        }

        TopologyComparators.LatencyComparator comparator = new TopologyComparators.LatencyComparator(latencies);

        result.sort(comparator);

        return result;
    }

    /*
     * Establish connections asynchronously.
     */
    private AsyncConnections getConnections(Iterable nodes) {

        AsyncConnections connections = new AsyncConnections();

        for (RedisNodeDescription node : nodes) {

            RedisURI redisURI = node.getUri();
            String message = String.format("Unable to connect to %s", redisURI);
            try {
                CompletableFuture> connectionFuture = nodeConnectionFactory
                        .connectToNodeAsync(CODEC, redisURI);

                CompletableFuture> sync = new CompletableFuture<>();

                connectionFuture.whenComplete((connection, throwable) -> {

                    if (throwable != null) {

                        if (throwable instanceof RedisConnectionException) {
                            if (logger.isDebugEnabled()) {
                                logger.debug(throwable.getMessage(), throwable);
                            } else {
                                logger.warn(throwable.getMessage());
                            }
                        } else {
                            logger.warn(message, throwable);
                        }

                        sync.completeExceptionally(new RedisConnectionException(message, throwable));
                    } else {
                        connection.async().clientSetname("lettuce#MasterSlaveTopologyRefresh");
                        sync.complete(connection);
                    }
                });

                connections.addConnection(redisURI, sync);
            } catch (RuntimeException e) {
                logger.warn(String.format(message, redisURI), e);
            }
        }

        return connections;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy