这就是 JedisConnectionFactory 源码。
/* * Copyright 2011-2015 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 org.springframework.data.redis.connection.jedis;import java.lang.reflect.Method;import java.util.Collection;import java.util.Collections;import java.util.LinkedHashSet;import java.util.Set;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.springframework.beans.factory.DisposableBean;import org.springframework.beans.factory.InitializingBean;import org.springframework.dao.DataAccessException;import org.springframework.dao.InvalidDataAccessResourceUsageException;import org.springframework.data.redis.ExceptionTranslationStrategy;import org.springframework.data.redis.PassThroughExceptionTranslationStrategy;import org.springframework.data.redis.RedisConnectionFailureException;import org.springframework.data.redis.connection.RedisConnection;import org.springframework.data.redis.connection.RedisConnectionFactory;import org.springframework.data.redis.connection.RedisNode;import org.springframework.data.redis.connection.RedisSentinelConfiguration;import org.springframework.data.redis.connection.RedisSentinelConnection;import org.springframework.util.Assert;import org.springframework.util.CollectionUtils;import org.springframework.util.ReflectionUtils;import org.springframework.util.StringUtils;import redis.clients.jedis.Jedis;import redis.clients.jedis.JedisPool;import redis.clients.jedis.JedisPoolConfig;import redis.clients.jedis.JedisSentinelPool;import redis.clients.jedis.JedisShardInfo;import redis.clients.jedis.Protocol;import redis.clients.util.Pool;/** * Connection factory creating Jedis based connections. * * @author Costin Leau * @author Thomas Darimont * @author Christoph Strobl */public class JedisConnectionFactory implements InitializingBean, DisposableBean, RedisConnectionFactory { private final static Log log = LogFactory.getLog(JedisConnectionFactory.class); private static final ExceptionTranslationStrategy EXCEPTION_TRANSLATION = new PassThroughExceptionTranslationStrategy( JedisConverters.exceptionConverter()); private static final Method SET_TIMEOUT_METHOD; private static final Method GET_TIMEOUT_METHOD; static { // We need to configure Jedis socket timeout via reflection since the method-name was changed between releases. Method setTimeoutMethodCandidate = ReflectionUtils.findMethod(JedisShardInfo.class, "setTimeout", int.class); if (setTimeoutMethodCandidate == null) { // Jedis V 2.7.x changed the setTimeout method to setSoTimeout setTimeoutMethodCandidate = ReflectionUtils.findMethod(JedisShardInfo.class, "setSoTimeout", int.class); } SET_TIMEOUT_METHOD = setTimeoutMethodCandidate; Method getTimeoutMethodCandidate = ReflectionUtils.findMethod(JedisShardInfo.class, "getTimeout"); if (getTimeoutMethodCandidate == null) { getTimeoutMethodCandidate = ReflectionUtils.findMethod(JedisShardInfo.class, "getSoTimeout"); } GET_TIMEOUT_METHOD = getTimeoutMethodCandidate; } private JedisShardInfo shardInfo; private String hostName = "localhost"; private int port = Protocol.DEFAULT_PORT; private int timeout = Protocol.DEFAULT_TIMEOUT; private String password; private boolean usePool = true; private Poolpool; private JedisPoolConfig poolConfig = new JedisPoolConfig(); private int dbIndex = 0; private boolean convertPipelineAndTxResults = true; private RedisSentinelConfiguration sentinelConfig; /** * Constructs a new JedisConnectionFactory
instance with default settings (default connection pooling, no * shard information). */ public JedisConnectionFactory() {} /** * Constructs a newJedisConnectionFactory
instance. Will override the other connection parameters passed * to the factory. * * @param shardInfo shard information */ public JedisConnectionFactory(JedisShardInfo shardInfo) { this.shardInfo = shardInfo; } /** * Constructs a newJedisConnectionFactory
instance using the given pool configuration. * * @param poolConfig pool configuration */ public JedisConnectionFactory(JedisPoolConfig poolConfig) { this(null, poolConfig); } /** * Constructs a new {@link JedisConnectionFactory} instance using the given {@link JedisPoolConfig} applied to * {@link JedisSentinelPool}. * * @param sentinelConfig * @since 1.4 */ public JedisConnectionFactory(RedisSentinelConfiguration sentinelConfig) { this(sentinelConfig, null); } /** * Constructs a new {@link JedisConnectionFactory} instance using the given {@link JedisPoolConfig} applied to * {@link JedisSentinelPool}. * * @param sentinelConfig * @param poolConfig pool configuration. Defaulted to new instance if {@literal null}. * @since 1.4 */ public JedisConnectionFactory(RedisSentinelConfiguration sentinelConfig, JedisPoolConfig poolConfig) { this.sentinelConfig = sentinelConfig; this.poolConfig = poolConfig != null ? poolConfig : new JedisPoolConfig(); } /** * Returns a Jedis instance to be used as a Redis connection. The instance can be newly created or retrieved from a * pool. * * @return Jedis instance ready for wrapping into a {@link RedisConnection}. */ protected Jedis fetchJedisConnector() { try { if (usePool && 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 RedisConnectionFailureException("Cannot get Jedis connection", ex); } } /** * Post process a newly retrieved connection. Useful for decorating or executing initialization commands on a new * connection. This implementation simply returns the connection. * * @param connection * @return processed connection */ protected JedisConnection postProcessConnection(JedisConnection connection) { return connection; } /* * (non-Javadoc) * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() */ public void afterPropertiesSet() { if (shardInfo == null) { shardInfo = new JedisShardInfo(hostName, port); if (StringUtils.hasLength(password)) { shardInfo.setPassword(password); } if (timeout > 0) { setTimeoutOn(shardInfo, timeout); } } if (usePool) { this.pool = createPool(); } } private PoolcreatePool() { if (isRedisSentinelAware()) { return createRedisSentinelPool(this.sentinelConfig); } return createRedisPool(); } /** * Creates {@link JedisSentinelPool}. * * @param config * @return * @since 1.4 */ protected Pool createRedisSentinelPool(RedisSentinelConfiguration config) { return new JedisSentinelPool(config.getMaster().getName(), convertToJedisSentinelSet(config.getSentinels()), getPoolConfig() != null ? getPoolConfig() : new JedisPoolConfig(), getTimeoutFrom(getShardInfo()), getShardInfo().getPassword()); } /** * Creates {@link JedisPool}. * * @return * @since 1.4 */ protected Pool createRedisPool() { return new JedisPool(getPoolConfig(), getShardInfo().getHost(), getShardInfo().getPort(), getTimeoutFrom(getShardInfo()), getShardInfo().getPassword()); } /* * (non-Javadoc) * @see org.springframework.beans.factory.DisposableBean#destroy() */ public void destroy() { if (usePool && pool != null) { try { pool.destroy(); } catch (Exception ex) { log.warn("Cannot properly close Jedis pool", ex); } pool = null; } } /* * (non-Javadoc) * @see org.springframework.data.redis.connection.RedisConnectionFactory#getConnection() */ public JedisConnection getConnection() { Jedis jedis = fetchJedisConnector(); JedisConnection connection = (usePool ? new JedisConnection(jedis, pool, dbIndex) : new JedisConnection(jedis, null, dbIndex)); connection.setConvertPipelineAndTxResults(convertPipelineAndTxResults); return postProcessConnection(connection); } /* * (non-Javadoc) * @see org.springframework.dao.support.PersistenceExceptionTranslator#translateExceptionIfPossible(java.lang.RuntimeException) */ public DataAccessException translateExceptionIfPossible(RuntimeException ex) { return EXCEPTION_TRANSLATION.translate(ex); } /** * 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; } /** * Indicates the use of a connection pool. * * @return Returns the use of connection pooling. */ public boolean getUsePool() { return usePool; } /** * Turns on or off the use of connection pooling. * * @param usePool The usePool to set. */ public void setUsePool(boolean usePool) { this.usePool = usePool; } /** * 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) { Assert.isTrue(index >= 0, "invalid DB index (a positive index required)"); this.dbIndex = index; } /** * Specifies if pipelined results should be converted to the expected data type. If false, results of * {@link JedisConnection#closePipeline()} and {@link JedisConnection#exec()} will be of the type returned by the * Jedis driver * * @return Whether or not to convert pipeline and tx results */ public boolean getConvertPipelineAndTxResults() { return convertPipelineAndTxResults; } /** * Specifies if pipelined results should be converted to the expected data type. If false, results of * {@link JedisConnection#closePipeline()} and {@link JedisConnection#exec()} will be of the type returned by the * Jedis driver * * @param convertPipelineAndTxResults Whether or not to convert pipeline and tx results */ public void setConvertPipelineAndTxResults(boolean convertPipelineAndTxResults) { this.convertPipelineAndTxResults = convertPipelineAndTxResults; } /** * @return true when {@link RedisSentinelConfiguration} is present. * @since 1.4 */ public boolean isRedisSentinelAware() { return sentinelConfig != null; } /* (non-Javadoc) * @see org.springframework.data.redis.connection.RedisConnectionFactory#getSentinelConnection() */ @Override public RedisSentinelConnection getSentinelConnection() { if (!isRedisSentinelAware()) { throw new InvalidDataAccessResourceUsageException("No Sentinels configured"); } return new JedisSentinelConnection(getActiveSentinel()); } private Jedis getActiveSentinel() { Assert.notNull(this.sentinelConfig); for (RedisNode node : this.sentinelConfig.getSentinels()) { Jedis jedis = new Jedis(node.getHost(), node.getPort()); if (jedis.ping().equalsIgnoreCase("pong")) { return jedis; } } throw new InvalidDataAccessResourceUsageException("no sentinel found"); } private Set convertToJedisSentinelSet(Collection nodes) { if (CollectionUtils.isEmpty(nodes)) { return Collections.emptySet(); } Set convertedNodes = new LinkedHashSet (nodes.size()); for (RedisNode node : nodes) { if (node != null) { convertedNodes.add(node.asString()); } } return convertedNodes; } private void setTimeoutOn(JedisShardInfo shardInfo, int timeout) { ReflectionUtils.invokeMethod(SET_TIMEOUT_METHOD, shardInfo, timeout); } private int getTimeoutFrom(JedisShardInfo shardInfo) { return (Integer) ReflectionUtils.invokeMethod(GET_TIMEOUT_METHOD, shardInfo); }}
先看下Pool这个连接池对象。
package redis.clients.util;import java.io.Closeable;import org.apache.commons.pool2.PooledObjectFactory;import org.apache.commons.pool2.impl.GenericObjectPool;import org.apache.commons.pool2.impl.GenericObjectPoolConfig;import redis.clients.jedis.exceptions.JedisConnectionException;import redis.clients.jedis.exceptions.JedisException;public abstract class Poolimplements Closeable { protected GenericObjectPool internalPool; /** * Using this constructor means you have to set and initialize the internalPool yourself. */ public Pool() { } @Override public void close() { destroy(); } public boolean isClosed() { return this.internalPool.isClosed(); } public Pool(final GenericObjectPoolConfig poolConfig, PooledObjectFactory factory) { initPool(poolConfig, factory); } public void initPool(final GenericObjectPoolConfig poolConfig, PooledObjectFactory factory) { if (this.internalPool != null) { try { closeInternalPool(); } catch (Exception e) { } } this.internalPool = new GenericObjectPool (factory, poolConfig); } public T getResource() { try { return internalPool.borrowObject(); } catch (Exception e) { throw new JedisConnectionException("Could not get a resource from the pool", e); } } /** * @deprecated starting from Jedis 3.0 this method won't exist. Resouce cleanup should be done * using @see {@link redis.clients.jedis.Jedis#close()} */ @Deprecated public void returnResourceObject(final T resource) { if (resource == null) { return; } try { internalPool.returnObject(resource); } catch (Exception e) { throw new JedisException("Could not return the resource to the pool", e); } } public void returnBrokenResource(final T resource) { if (resource != null) { returnBrokenResourceObject(resource); } } public void returnResource(final T resource) { if (resource != null) { returnResourceObject(resource); } } public void destroy() { closeInternalPool(); } protected void returnBrokenResourceObject(final T resource) { try { internalPool.invalidateObject(resource); } catch (Exception e) { throw new JedisException("Could not return the resource to the pool", e); } } protected void closeInternalPool() { try { internalPool.close(); } catch (Exception e) { throw new JedisException("Could not destroy the pool", e); } } public int getNumActive() { if (poolInactive()) { return -1; } return this.internalPool.getNumActive(); } public int getNumIdle() { if (poolInactive()) { return -1; } return this.internalPool.getNumIdle(); } public int getNumWaiters() { if (poolInactive()) { return -1; } return this.internalPool.getNumWaiters(); } public long getMeanBorrowWaitTimeMillis() { if (poolInactive()) { return -1; } return this.internalPool.getMeanBorrowWaitTimeMillis(); } public long getMaxBorrowWaitTimeMillis() { if (poolInactive()) { return -1; } return this.internalPool.getMaxBorrowWaitTimeMillis(); } private boolean poolInactive() { return this.internalPool == null || this.internalPool.isClosed(); } public void addObjects(int count) { try { for (int i = 0; i < count ; i++) { this.internalPool.addObject(); } } catch (Exception e) { throw new JedisException("Error trying to add idle objects", e); } }}
哎呀还有个接口:
package java.io;import java.io.IOException;/** * A {@code Closeable} is a source or destination of data that can be closed. * The close method is invoked to release resources that the object is * holding (such as open files). * * @since 1.5 */public interface Closeable extends AutoCloseable { /** * Closes this stream and releases any system resources associated * with it. If the stream is already closed then invoking this * method has no effect. * *As noted in {@link AutoCloseable#close()}, cases where the * close may fail require careful attention. It is strongly advised * to relinquish the underlying resources and to internally * mark the {@code Closeable} as closed, prior to throwing * the {@code IOException}. * * @throws IOException if an I/O error occurs */ public void close() throws IOException;}
Pool实现了自动关闭连接接口,pool又是怎样实现的呢?
@Override public void close() { destroy(); } public void destroy() { closeInternalPool(); } protected void closeInternalPool() { try { internalPool.close(); } catch (Exception e) { throw new JedisException("Could not destroy the pool", e); } } protected GenericObjectPoolinternalPool;
这里又有个他GenericObjectPool什么鬼接着扒一扒:
通过这会博客,明白了通用连接池对象的原理,
edis pool 和 spring 实现基本类似,都是用了GenericObjectPool进行管理i。