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

com.redislabs.picocliredis.RedisOptions Maven / Gradle / Ivy

There is a newer version: 2.0.2
Show newest version
package com.redislabs.picocliredis;

import java.io.File;
import java.time.Duration;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;

import com.redislabs.lettusearch.RediSearchClient;
import com.redislabs.lettusearch.StatefulRediSearchConnection;

import io.lettuce.core.AbstractRedisClient;
import io.lettuce.core.ClientOptions;
import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.SslOptions;
import io.lettuce.core.api.StatefulConnection;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.api.async.RedisAsyncCommands;
import io.lettuce.core.api.reactive.RedisReactiveCommands;
import io.lettuce.core.api.sync.BaseRedisCommands;
import io.lettuce.core.api.sync.RedisCommands;
import io.lettuce.core.cluster.ClusterClientOptions;
import io.lettuce.core.cluster.RedisClusterClient;
import io.lettuce.core.cluster.api.StatefulRedisClusterConnection;
import io.lettuce.core.cluster.api.async.RedisClusterAsyncCommands;
import io.lettuce.core.cluster.api.reactive.RedisClusterReactiveCommands;
import io.lettuce.core.cluster.api.sync.RedisClusterCommands;
import io.lettuce.core.event.DefaultEventPublisherOptions;
import io.lettuce.core.event.metrics.CommandLatencyEvent;
import io.lettuce.core.metrics.DefaultCommandLatencyCollectorOptions;
import io.lettuce.core.pubsub.StatefulRedisPubSubConnection;
import io.lettuce.core.resource.ClientResources;
import io.lettuce.core.resource.DefaultClientResources;
import io.lettuce.core.support.ConnectionPoolSupport;
import lombok.extern.slf4j.Slf4j;
import picocli.CommandLine.Option;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisSentinelPool;
import redis.clients.jedis.Protocol;
import redis.clients.jedis.util.Pool;

@Slf4j
public class RedisOptions {

	public enum Api {
		ASYNC, REACTIVE, SYNC
	}

	public static enum RedisDriver {
		JEDIS, LETTUCE
	}

	@Option(names = "--driver", description = "Redis driver: ${COMPLETION-CANDIDATES} (default: ${DEFAULT-VALUE})", paramLabel = "")
	private RedisDriver driver = RedisDriver.LETTUCE;
	@Option(names = { "-s",
			"--servers" }, arity = "1..*", description = "Server endpoints (default: ${DEFAULT-VALUE})", paramLabel = "", hideParamSyntax = true)
	private List servers = new Servers(new Server());
	@Option(names = "--sentinel", description = "Sentinel master", paramLabel = "")
	private String sentinelMaster;
	@Option(names = "--auth", arity = "0..1", interactive = true, description = "Database login password", paramLabel = "")
	private String password;
	@Option(names = "--db", description = "Redis database number", paramLabel = "")
	private int database = 0;
	@Option(names = "--client", description = "Redis client name (default: ${DEFAULT-VALUE})", paramLabel = "")
	private String clientName = "picocli-redis";
	@Option(names = "--ssl", description = "SSL connection")
	private boolean ssl;
	@Option(names = "--cluster", description = "Connect to a Redis cluster")
	private boolean cluster;
	@Option(names = "--max-redirects", description = "Max cluster redirects (default: ${DEFAULT-VALUE})", paramLabel = "")
	private int maxRedirects = ClusterClientOptions.DEFAULT_MAX_REDIRECTS;
	@Option(names = "--pool-max-total", description = "Max connections; -1 for no limit (default: ${DEFAULT-VALUE})", paramLabel = "")
	private int maxTotal = GenericObjectPoolConfig.DEFAULT_MAX_TOTAL;
	@Option(names = "--pool-min-idle", description = "Min idle connections (default: ${DEFAULT-VALUE})", paramLabel = "")
	private int minIdle = GenericObjectPoolConfig.DEFAULT_MIN_IDLE;
	@Option(names = "--pool-max-idle", description = "Max idle connections; -1 for no limit (default: ${DEFAULT-VALUE})", paramLabel = "")
	private int maxIdle = GenericObjectPoolConfig.DEFAULT_MAX_IDLE;
	@Option(names = "--pool-max-wait", description = "Max duration; -1 for infinite (default: ${DEFAULT-VALUE})", paramLabel = "")
	private long maxWait = GenericObjectPoolConfig.DEFAULT_MAX_WAIT_MILLIS;
	@Option(names = "--connect-timeout", description = "Connect timeout (default: ${DEFAULT-VALUE})", paramLabel = "")
	private int connectTimeout = Protocol.DEFAULT_TIMEOUT;
	@Option(names = "--socket-timeout", description = "Socket timeout (default: ${DEFAULT-VALUE})", paramLabel = "")
	private int socketTimeout = Protocol.DEFAULT_TIMEOUT;
	@Option(names = "--api", description = "API: ${COMPLETION-CANDIDATES} (default: ${DEFAULT-VALUE})", paramLabel = "")
	private Api api = Api.ASYNC;
	@Option(names = "--ks", description = "Path to keystore", paramLabel = "")
	private File keystore;
	@Option(names = "--ks-password", arity = "0..1", interactive = true, description = "Keystore password", paramLabel = "")
	private String keystorePassword;
	@Option(names = "--ts", description = "Path to truststore", paramLabel = "")
	private File truststore;
	@Option(names = "--ts-password", arity = "0..1", interactive = true, description = "Truststore password", paramLabel = "")
	private String truststorePassword;
	@Option(names = "--comp-threads", description = "Number of computation threads (default: ${DEFAULT-VALUE})", paramLabel = "")
	private int computationThreadPoolSize = DefaultClientResources.DEFAULT_COMPUTATION_THREADS;
	@Option(names = "--io-threads", description = "Number of threads for I/O operations (default: ${DEFAULT-VALUE})", paramLabel = "")
	private int ioThreadPoolSize = DefaultClientResources.DEFAULT_IO_THREADS;
	@Option(names = "--command-timeout", description = "Timeout for sync command execution (default: ${DEFAULT-VALUE})", paramLabel = "")
	private long commandTimeout = RedisURI.DEFAULT_TIMEOUT;
	@Option(names = "--fire-and-forget", description = "Do not wait for response in async Lettuce")
	private boolean fireAndForget;
	@Option(names = "--metrics", description = "Show metrics")
	private boolean showMetrics;
	@Option(names = "--publish-on-sched", description = "Enable publish on scheduler (default: ${DEFAULT-VALUE})")
	private boolean publishOnScheduler = ClientOptions.DEFAULT_PUBLISH_ON_SCHEDULER;
	@Option(names = "--no-auto-reconnect", description = "Disable auto-reconnect")
	private boolean noAutoReconnect = false;
	@Option(names = "--request-queue", description = "Per-connection request queue size (default: max)", paramLabel = "")
	private int requestQueueSize = ClientOptions.DEFAULT_REQUEST_QUEUE_SIZE;
	@Option(names = "--ssl-provider", description = "SSL provider: ${COMPLETION-CANDIDATES} (default: ${DEFAULT-VALUE})", paramLabel = "")
	private SslProvider sslProvider = SslProvider.Jdk;
	
	public boolean isFireAndForget() {
		return fireAndForget;
	}

	public int getDatabase() {
		return database;
	}

	public List getServers() {
		return servers;
	}

	public boolean isCluster() {
		return cluster;
	}

	public Api getApi() {
		return api;
	}

	public long getCommandTimeout() {
		return commandTimeout;
	}

	@SuppressWarnings("rawtypes")
	public  T configure(T poolConfig) {
		poolConfig.setMaxTotal(maxTotal);
		poolConfig.setMaxIdle(maxIdle);
		poolConfig.setMinIdle(minIdle);
		poolConfig.setMaxWaitMillis(maxWait);
		poolConfig.setJmxEnabled(false);
		return poolConfig;
	}

	public boolean hasPassword() {
		return !isEmpty(password);
	}

	public boolean isSentinel() {
		return !isEmpty(sentinelMaster);
	}

	private boolean isEmpty(String string) {
		return string == null || string.length() == 0;
	}

	public boolean isJedis() {
		return driver == RedisDriver.JEDIS;
	}

	public JedisCluster jedisCluster() {
		Set hostAndPort = servers.stream().map(s -> new HostAndPort(s.getHost(), s.getPort()))
				.collect(Collectors.toSet());
		if (hasPassword()) {
			return new JedisCluster(hostAndPort, connectTimeout, socketTimeout, maxRedirects, password,
					configure(new JedisPoolConfig()));
		}
		return new JedisCluster(hostAndPort, connectTimeout, socketTimeout, maxRedirects,
				configure(new JedisPoolConfig()));
	}

	public Pool jedisPool() {
		JedisPoolConfig poolConfig = configure(new JedisPoolConfig());
		if (isSentinel()) {
			Set addresses = servers.stream().map(s -> s.address()).collect(Collectors.toSet());
			return new JedisSentinelPool(sentinelMaster, addresses, poolConfig, connectTimeout, socketTimeout, password,
					database, clientName);
		}
		String host = servers.get(0).getHost();
		int port = servers.get(0).getPort();
		log.debug("Creating Jedis connection pool for {}:{} with {}", host, port, poolConfig);
		return new JedisPool(poolConfig, host, port, connectTimeout, socketTimeout, password, database, clientName);
	}

	public GenericObjectPool> pool(AbstractRedisClient client) {
		return pool(lettuceConnectionSupplier(client));
	}

	private GenericObjectPool> pool(
			Supplier> supplier) {
		return ConnectionPoolSupport.createGenericObjectPool(supplier, configure(new GenericObjectPoolConfig<>()));
	}

	private Supplier> lettuceConnectionSupplier(AbstractRedisClient client) {
		if (client instanceof RediSearchClient) {
			return ((RediSearchClient) client)::connect;
		}
		if (client instanceof RedisClusterClient) {
			return ((RedisClusterClient) client)::connect;
		}
		return ((RedisClient) client)::connect;
	}

	public AbstractRedisClient lettuceClient() {
		if (cluster) {
			return redisClusterClient();
		}
		return redisClient();
	}

	public RedisClient redisClient() {
		log.debug("Creating RedisClient");
		RedisClient client = RedisClient.create(clientResources(), redisURI());
		client.setOptions(clientOptions(ssl, ClientOptions.builder()));
		return client;
	}

	public RedisClusterClient redisClusterClient() {
		log.debug("Creating RedisClusterClient");
		List uris = servers.stream().map(s -> redisURI(s)).collect(Collectors.toList());
		RedisClusterClient client = RedisClusterClient.create(clientResources(), uris);
		ClusterClientOptions.Builder builder = ClusterClientOptions.builder();
		builder.maxRedirects(maxRedirects);
		client.setOptions((ClusterClientOptions) clientOptions(ssl, builder));
		return client;
	}

	public RediSearchClient rediSearchClient() {
		log.debug("Creating RediSearchClient");
		RediSearchClient client = RediSearchClient.create(clientResources(), redisURI());
		client.setOptions(clientOptions(ssl, ClientOptions.builder()));
		return client;
	}

	private RedisURI redisURI() {
		Server server = servers.get(0);
		if (isSentinel()) {
			RedisURI.Builder builder = RedisURI.Builder.sentinel(server.getHost(), server.getPort(), sentinelMaster);
			servers.forEach(s -> builder.withSentinel(s.getHost(), s.getPort()));
			return redisURI(builder);
		}
		return redisURI(server);
	}

	private RedisURI redisURI(Server server) {
		RedisURI.Builder builder = RedisURI.Builder.redis(server.getHost()).withPort(server.getPort());
		return redisURI(builder);
	}

	private RedisURI redisURI(RedisURI.Builder builder) {
		builder.withClientName(clientName).withDatabase(database).withTimeout(Duration.ofSeconds(commandTimeout));
		if (hasPassword()) {
			builder.withPassword(password);
		}
		if (ssl) {
			builder.withSsl(ssl);
		}
		return builder.build();
	}

	@SuppressWarnings("rawtypes")
	public Function lettuceApi() {
		if (cluster) {
			return lettuceClusterApi();
		}
		return lettuceStandaloneApi();
	}

	@SuppressWarnings("rawtypes")
	public Function lettuceAsyncApi() {
		if (cluster) {
			return lettuceClusterAsyncApi();
		}
		return lettuceStandaloneAsyncApi();
	}

	@SuppressWarnings("rawtypes")
	private Function lettuceStandaloneApi() {
		switch (api) {
		case SYNC:
			return lettuceStandaloneSyncApi();
		case REACTIVE:
			return lettuceStandaloneReactiveApi();
		default:
			return lettuceStandaloneAsyncApi();
		}
	}

	private Function, RedisCommands> lettuceStandaloneSyncApi() {
		return (Function, RedisCommands>) StatefulRedisConnection::sync;
	}

	private Function, RedisReactiveCommands> lettuceStandaloneReactiveApi() {
		return (Function, RedisReactiveCommands>) StatefulRedisConnection::reactive;
	}

	private Function, RedisAsyncCommands> lettuceStandaloneAsyncApi() {
		return (Function, RedisAsyncCommands>) StatefulRedisConnection::async;
	}

	@SuppressWarnings("rawtypes")
	private Function lettuceClusterApi() {
		switch (api) {
		case SYNC:
			return lettuceClusterSyncApi();
		case REACTIVE:
			return lettuceClusterReactiveApi();
		default:
			return lettuceClusterAsyncApi();
		}
	}

	private Function, RedisClusterAsyncCommands> lettuceClusterAsyncApi() {
		return (Function, RedisClusterAsyncCommands>) StatefulRedisClusterConnection::async;
	}

	private Function, RedisClusterReactiveCommands> lettuceClusterReactiveApi() {
		return (Function, RedisClusterReactiveCommands>) StatefulRedisClusterConnection::reactive;
	}

	private Function, RedisClusterCommands> lettuceClusterSyncApi() {
		return (Function, RedisClusterCommands>) StatefulRedisClusterConnection::sync;
	}

	public Function, ?> lettuSearchApi() {
		switch (api) {
		case SYNC:
			return StatefulRediSearchConnection::sync;
		case REACTIVE:
			return StatefulRediSearchConnection::reactive;
		default:
			return StatefulRediSearchConnection::async;
		}
	}

	public BaseRedisCommands redisCommands() {
		if (cluster) {
			return redisClusterClient().connect().sync();
		}
		return redisClient().connect().sync();
	}

	public StatefulConnection statefulConnection() {
		if (cluster) {
			return redisClusterClient().connect();
		}
		return redisClient().connect();
	}

	public GenericObjectPool> lettucePool() {
		if (cluster) {
			return pool(redisClusterClient()::connect);
		}
		return pool(redisClient()::connect);
	}

	public StatefulRedisPubSubConnection statefulRedisPubSubConnection() {
		if (cluster) {
			return redisClusterClient().connectPubSub();
		}
		return redisClient().connectPubSub();
	}

	public ClientOptions clientOptions(boolean ssl, ClientOptions.Builder builder) {
		if (ssl) {
			builder.sslOptions(sslOptions());
		}
		builder.publishOnScheduler(publishOnScheduler);
		builder.autoReconnect(!noAutoReconnect);
		builder.requestQueueSize(requestQueueSize);
		return builder.build();
	}

	private SslOptions sslOptions() {
		SslOptions.Builder builder = SslOptions.builder();
		switch (sslProvider) {
		case OpenSsl:
			builder.openSslProvider();
			break;
		default:
			builder.jdkSslProvider();
			break;
		}
		if (keystore != null) {
			if (keystorePassword == null) {
				builder.keystore(keystore);
			} else {
				builder.keystore(keystore, keystorePassword.toCharArray());
			}
		}
		if (truststore != null) {
			if (truststorePassword == null) {
				builder.truststore(truststore);
			} else {
				builder.truststore(truststore, truststorePassword);
			}
		}
		return builder.build();
	}

	public ClientResources clientResources() {
		DefaultClientResources.Builder builder = DefaultClientResources.builder();
		builder.computationThreadPoolSize(computationThreadPoolSize);
		builder.ioThreadPoolSize(ioThreadPoolSize);
		if (showMetrics) {
			builder.commandLatencyCollectorOptions(DefaultCommandLatencyCollectorOptions.builder().enable().build());
			builder.commandLatencyPublisherOptions(
					DefaultEventPublisherOptions.builder().eventEmitInterval(Duration.ofSeconds(1)).build());
		}
		ClientResources resources = builder.build();
		if (showMetrics) {
			resources.eventBus().get().filter(redisEvent -> redisEvent instanceof CommandLatencyEvent)
					.cast(CommandLatencyEvent.class).subscribe(e -> System.out.println(e.getLatencies()));
		}
		return resources;
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy