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

com.netflix.dyno.jedis.DynoJedisUtils Maven / Gradle / Ivy

package com.netflix.dyno.jedis;

import com.netflix.discovery.EurekaClient;
import com.netflix.dyno.connectionpool.ConnectionFactory;
import com.netflix.dyno.connectionpool.ConnectionPool;
import com.netflix.dyno.connectionpool.ConnectionPoolConfiguration;
import com.netflix.dyno.connectionpool.ConnectionPoolMonitor;
import com.netflix.dyno.connectionpool.Host;
import com.netflix.dyno.connectionpool.HostSupplier;
import com.netflix.dyno.connectionpool.TokenMapSupplier;
import com.netflix.dyno.connectionpool.exception.DynoConnectException;
import com.netflix.dyno.connectionpool.exception.NoAvailableHostsException;
import com.netflix.dyno.connectionpool.impl.ConnectionPoolConfigurationImpl;
import com.netflix.dyno.connectionpool.impl.ConnectionPoolImpl;
import com.netflix.dyno.connectionpool.impl.lb.HostToken;
import com.netflix.dyno.connectionpool.impl.lb.HttpEndpointBasedTokenMapSupplier;
import com.netflix.dyno.contrib.DynoOPMonitor;
import com.netflix.dyno.contrib.EurekaHostsSupplier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;

import javax.inject.Singleton;
import javax.net.ssl.SSLSocketFactory;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Stream;

@Singleton
public class DynoJedisUtils {


    private static final Logger logger = LoggerFactory.getLogger(DynoJedisClient.class);

    public static void updateConnectionPoolConfig(ConnectionPoolConfigurationImpl cpConfig,
                                                  HostSupplier hostSupplier, TokenMapSupplier tokenMapSupplier,
                                                  EurekaClient discoveryClient, String clusterName) {
        if (hostSupplier == null) {
            if (discoveryClient == null) {
                throw new DynoConnectException("HostSupplier not provided. Cannot initialize EurekaHostsSupplier "
                        + "which requires a DiscoveryClient");
            } else {
                hostSupplier = new EurekaHostsSupplier(clusterName, discoveryClient);
            }
        }
        cpConfig.withHostSupplier(hostSupplier);
        if (tokenMapSupplier != null)
            cpConfig.withTokenSupplier(tokenMapSupplier);
        setLoadBalancingStrategy(cpConfig);
        setHashtagConnectionPool(hostSupplier, cpConfig);
    }

    public static ConnectionPool createConnectionPool(String appName, DynoOPMonitor opMonitor,
                                                             ConnectionPoolMonitor cpMonitor, ConnectionPoolConfiguration cpConfig,
                                                             SSLSocketFactory sslSocketFactory) {
        JedisConnectionFactory connFactory = new JedisConnectionFactory(opMonitor, sslSocketFactory);

        return startConnectionPool(appName, connFactory, cpConfig, cpMonitor);
    }

    private static ConnectionPool startConnectionPool(String appName, ConnectionFactory connFactory,
                                                             ConnectionPoolConfiguration cpConfig, ConnectionPoolMonitor cpMonitor) {

        final ConnectionPool pool = new ConnectionPoolImpl<>(connFactory, cpConfig, cpMonitor);

        try {
            logger.info("Starting connection pool for app " + appName);

            pool.start().get();

            Runtime.getRuntime().addShutdownHook(new Thread(() -> pool.shutdown()));
        } catch (NoAvailableHostsException e) {
            if (cpConfig.getFailOnStartupIfNoHosts()) {
                throw new RuntimeException(e);
            }

            logger.warn("UNABLE TO START CONNECTION POOL -- IDLING");

            pool.idle();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        return pool;
    }

    private static void setLoadBalancingStrategy(ConnectionPoolConfigurationImpl config) {
        if (ConnectionPoolConfiguration.LoadBalancingStrategy.TokenAware == config.getLoadBalancingStrategy()) {
            if (config.getTokenSupplier() == null) {
                logger.warn(
                        "TOKEN AWARE selected and no token supplier found, using default HttpEndpointBasedTokenMapSupplier()");
                config.withTokenSupplier(new HttpEndpointBasedTokenMapSupplier());
            }

            if (config.getLocalRack() == null && config.localZoneAffinity()) {
                String warningMessage = "DynoJedisClient for app=[" + config.getName()
                        + "] is configured for local rack affinity "
                        + "but cannot determine the local rack! DISABLING rack affinity for this instance. "
                        + "To make the client aware of the local rack either use "
                        + "ConnectionPoolConfigurationImpl.setLocalRack() when constructing the client "
                        + "instance or ensure EC2_AVAILABILTY_ZONE is set as an environment variable, e.g. "
                        + "run with -DLOCAL_RACK=us-east-1c";
                config.setLocalZoneAffinity(false);
                logger.warn(warningMessage);
            }
        }
    }

    /**
     * Set the hash to the connection pool if is provided by Dynomite
     *
     * @param hostSupplier
     * @param config
     */
    private static void setHashtagConnectionPool(HostSupplier hostSupplier, ConnectionPoolConfigurationImpl config) {
        // Find the hosts from host supplier
        List hosts = (List) hostSupplier.getHosts();
        Collections.sort(hosts);

        // Take the token map supplier (aka the token topology from
        // Dynomite)
        TokenMapSupplier tokenMapSupplier = config.getTokenSupplier();

        // Create a list of host/Tokens
        List hostTokens;
        if (tokenMapSupplier != null) {
            Set hostSet = new HashSet(hosts);
            hostTokens = tokenMapSupplier.getTokens(hostSet);
            /* Dyno cannot reach the TokenMapSupplier endpoint,
             * therefore no nodes can be retrieved.
             */
            if (hostTokens.isEmpty()) {
                throw new DynoConnectException("No hosts in the TokenMapSupplier");
            }
        } else {
            throw new DynoConnectException("TokenMapSupplier not provided");
        }

        String hashtag = hostTokens.get(0).getHost().getHashtag();
        Stream htStream = hostTokens.stream().map(hostToken -> hostToken.getHost().getHashtag());

        if (hashtag == null) {
            htStream.filter(ht -> ht != null).findAny().ifPresent(ignore -> {
                logger.error("Hashtag mismatch across hosts");
                throw new RuntimeException("Hashtags are different across hosts");
            });
        } else {
            /**
             * Checking hashtag consistency from all Dynomite hosts. If
             * hashtags are not consistent, we need to throw an exception.
             */
            htStream.filter(ht -> !hashtag.equals(ht)).findAny().ifPresent(ignore -> {
                logger.error("Hashtag mismatch across hosts");
                throw new RuntimeException("Hashtags are different across hosts");
            });
        }

        if (hashtag != null) {
            config.withHashtag(hashtag);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy