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

io.lettuce.core.masterreplica.SentinelTopologyProvider 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.

The newest version!
package io.lettuce.core.masterreplica;

import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import reactor.core.publisher.Mono;
import reactor.util.function.Tuple2;
import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.codec.StringCodec;
import io.lettuce.core.internal.Exceptions;
import io.lettuce.core.internal.LettuceAssert;
import io.lettuce.core.models.role.RedisInstance;
import io.lettuce.core.models.role.RedisNodeDescription;
import io.lettuce.core.sentinel.api.StatefulRedisSentinelConnection;
import io.lettuce.core.sentinel.api.reactive.RedisSentinelReactiveCommands;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;

/**
 * Topology provider using Redis Sentinel and the Sentinel API.
 *
 * @author Mark Paluch
 * @since 4.1
 */
class SentinelTopologyProvider implements TopologyProvider {

    private static final InternalLogger logger = InternalLoggerFactory.getInstance(SentinelTopologyProvider.class);

    private final String masterId;

    private final RedisClient redisClient;

    private final RedisURI sentinelUri;

    private final Duration timeout;

    /**
     * Creates a new {@link SentinelTopologyProvider}.
     *
     * @param masterId must not be empty
     * @param redisClient must not be {@code null}.
     * @param sentinelUri must not be {@code null}.
     */
    public SentinelTopologyProvider(String masterId, RedisClient redisClient, RedisURI sentinelUri) {

        LettuceAssert.notEmpty(masterId, "MasterId must not be empty");
        LettuceAssert.notNull(redisClient, "RedisClient must not be null");
        LettuceAssert.notNull(sentinelUri, "Sentinel URI must not be null");

        this.masterId = masterId;
        this.redisClient = redisClient;
        this.sentinelUri = sentinelUri;
        this.timeout = sentinelUri.getTimeout();
    }

    @Override
    public List getNodes() {

        logger.debug("lookup topology for masterId {}", masterId);

        try {
            return getNodesAsync().get(timeout.toMillis(), TimeUnit.MILLISECONDS);
        } catch (Exception e) {
            throw Exceptions.bubble(e);
        }
    }

    @Override
    public CompletableFuture> getNodesAsync() {

        logger.debug("lookup topology for masterId {}", masterId);

        Mono> connect = Mono
                .fromFuture(redisClient.connectSentinelAsync(StringCodec.UTF8, sentinelUri));

        return connect.flatMap(this::getNodes).toFuture();
    }

    protected Mono> getNodes(StatefulRedisSentinelConnection connection) {

        RedisSentinelReactiveCommands reactive = connection.reactive();

        Mono, List>>> masterAndReplicas = reactive.master(masterId)
                .zipWith(reactive.replicas(masterId).collectList()).timeout(this.timeout).flatMap(tuple -> {
                    return ResumeAfter.close(connection).thenEmit(tuple);
                }).doOnError(e -> connection.closeAsync());

        return masterAndReplicas.map(tuple -> {

            List result = new ArrayList<>();

            result.add(toNode(tuple.getT1(), RedisInstance.Role.UPSTREAM));
            result.addAll(tuple.getT2().stream().filter(SentinelTopologyProvider::isAvailable)
                    .map(map -> toNode(map, RedisInstance.Role.REPLICA)).collect(Collectors.toList()));

            return result;
        });
    }

    private static boolean isAvailable(Map map) {

        String flags = map.get("flags");
        if (flags != null) {
            if (flags.contains("s_down") || flags.contains("o_down") || flags.contains("disconnected")) {
                return false;
            }
        }
        return true;
    }

    private RedisNodeDescription toNode(Map map, RedisInstance.Role role) {

        String ip = map.get("ip");
        String port = map.get("port");
        return new RedisMasterReplicaNode(ip, Integer.parseInt(port), sentinelUri, role);
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy