/*
* 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.mpush.cache.redis.connection;
import com.mpush.cache.redis.RedisServer;
import com.mpush.tools.config.data.RedisNode;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.*;
import redis.clients.util.Pool;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Connection factory creating <a href="http://github.com/xetorthio/jedis">Jedis</a> based connections.
*
* @author Costin Leau
* @author Thomas Darimont
* @author Christoph Strobl
* @author Mark Paluch
*/
public class RedisConnectionFactory {
private final static Logger log = LoggerFactory.getLogger(RedisConnectionFactory.class);
private JedisShardInfo shardInfo;
private String hostName = "localhost";
private int port = Protocol.DEFAULT_PORT;
private int timeout = Protocol.DEFAULT_TIMEOUT;
private String password;
private Pool<Jedis> pool;
private JedisPoolConfig poolConfig = new JedisPoolConfig();
private int dbIndex = 0;
private JedisCluster cluster;
private List<RedisNode> redisServers;
private boolean isCluster = false;
/**
* Constructs a new <code>JedisConnectionFactory</code> instance with default settings (default connection pooling, no
* shard information).
*/
public RedisConnectionFactory() {
}
/**
* Returns a Jedis instance to be used as a Redis connection. The instance can be newly created or retrieved from a
* pool.
*/
protected Jedis fetchJedisConnector() {
try {
if (pool != null) {
return pool.getResource();
}
Jedis jedis = new Jedis(getShardInfo());
// force initialization (see Jedis issue #82)
jedis.connect();
return jedis;
} catch (Exception ex) {
throw new RuntimeException("Cannot get Jedis connection", ex);
}
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
public void init() {
if (shardInfo == null) {
shardInfo = new JedisShardInfo(hostName, port);
if (StringUtils.isNotEmpty(password)) {
shardInfo.setPassword(password);
}
if (timeout > 0) {
shardInfo.setConnectionTimeout(timeout);
}
}
if (isCluster) {
this.cluster = createCluster();
} else {
this.pool = createPool();
}
}
private Pool<Jedis> createPool() {
return createRedisPool();
}
/**
* Creates {@link JedisPool}.
*
* @return
* @since 1.4
*/
protected Pool<Jedis> createRedisPool() {
return new JedisPool(getPoolConfig(), getShardInfo().getHost(), getShardInfo().getPort(),
getShardInfo().getSoTimeout(), getShardInfo().getPassword());
}
private JedisCluster createCluster() {
return createCluster(this.redisServers, this.poolConfig);
}
/**
* @param poolConfig can be {@literal null}.
* @return
* @since 1.7
*/
protected JedisCluster createCluster(List<RedisNode> servers, GenericObjectPoolConfig poolConfig) {
Set<HostAndPort> hostAndPort = servers
.stream()
.map(redisNode -> new HostAndPort(redisNode.host, redisNode.port))
.collect(Collectors.toSet());
int redirects = 5;
if (StringUtils.isNotEmpty(getPassword())) {
throw new IllegalArgumentException("Jedis does not support password protected Redis Cluster configurations!");
}
if (poolConfig != null) {
return new JedisCluster(hostAndPort, timeout, redirects, poolConfig);
}
return new JedisCluster(hostAndPort, timeout, redirects, poolConfig);
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.DisposableBean#destroy()
*/
public void destroy() {
if (pool != null) {
try {
pool.destroy();
} catch (Exception ex) {
log.warn("Cannot properly close Jedis pool", ex);
}
pool = null;
}
if (cluster != null) {
try {
cluster.close();
} catch (Exception ex) {
log.warn("Cannot properly close Jedis cluster", ex);
}
cluster = null;
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisConnectionFactory#getConnection()
*/
public Jedis getJedisConnection() {
return fetchJedisConnector();
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisConnectionFactory#getClusterConnection()
*/
public JedisCluster getClusterConnection() {
return cluster;
}
public boolean isCluster() {
return isCluster;
}
/**
* Returns the Redis hostName.
*
* @return Returns the hostName
*/
public String getHostName() {
return hostName;
}
/**
* Sets the Redis hostName.
*
* @param hostName The hostName to set.
*/
public void setHostName(String hostName) {
this.hostName = hostName;
}
/**
* Returns the password used for authenticating with the Redis server.
*
* @return password for authentication
*/
public String getPassword() {
return password;
}
/**
* Sets the password used for authenticating with the Redis server.
*
* @param password the password to set
*/
public void setPassword(String password) {
this.password = password;
}
/**
* Returns the port used to connect to the Redis instance.
*
* @return Redis port.
*/
public int getPort() {
return port;
}
/**
* Sets the port used to connect to the Redis instance.
*
* @param port Redis port
*/
public void setPort(int port) {
this.port = port;
}
/**
* Returns the shardInfo.
*
* @return Returns the shardInfo
*/
public JedisShardInfo getShardInfo() {
return shardInfo;
}
/**
* Sets the shard info for this factory.
*
* @param shardInfo The shardInfo to set.
*/
public void setShardInfo(JedisShardInfo shardInfo) {
this.shardInfo = shardInfo;
}
/**
* Returns the timeout.
*
* @return Returns the timeout
*/
public int getTimeout() {
return timeout;
}
/**
* @param timeout The timeout to set.
*/
public void setTimeout(int timeout) {
this.timeout = timeout;
}
/**
* Returns the poolConfig.
*
* @return Returns the poolConfig
*/
public JedisPoolConfig getPoolConfig() {
return poolConfig;
}
/**
* Sets the pool configuration for this factory.
*
* @param poolConfig The poolConfig to set.
*/
public void setPoolConfig(JedisPoolConfig poolConfig) {
this.poolConfig = poolConfig;
}
/**
* Returns the index of the database.
*
* @return Returns the database index
*/
public int getDatabase() {
return dbIndex;
}
/**
* Sets the index of the database used by this connection factory. Default is 0.
*
* @param index database index
*/
public void setDatabase(int index) {
this.dbIndex = index;
}
public void setCluster(boolean cluster) {
isCluster = cluster;
}
public void setRedisServers(List<RedisNode> redisServers) {
if (redisServers == null || redisServers.isEmpty()) {
throw new IllegalArgumentException("redis server node can not be empty, please check your conf.");
}
this.redisServers = redisServers;
this.hostName = redisServers.get(0).getHost();
this.port = redisServers.get(0).getPort();
}
}