关于jedis:聊聊jedis连接池参数配置

序本文次要钻研一下jedis连接池的参数配置 JedisConfigredis/clients/jedis/JedisPoolConfig.java public class JedisPoolConfig extends GenericObjectPoolConfig<Jedis> { public JedisPoolConfig() { // defaults to make your life with connection pool easier :) setTestWhileIdle(true); setMinEvictableIdleTimeMillis(60000); setTimeBetweenEvictionRunsMillis(30000); setNumTestsPerEvictionRun(-1); }}JedisPoolConfig继承了GenericObjectPoolConfig,同时默认配置了testWhileIdle为true(默认为false),minEvictableIdleTime为60s(默认为30分钟),timeBetweenEvictionRuns为30s(默认为-1),numTestsPerEvictionRun为-1(即检测所有闲暇连贯,默认值为3)GenericObjectPoolConfigorg/apache/commons/pool2/impl/GenericObjectPoolConfig.java public class GenericObjectPoolConfig<T> extends BaseObjectPoolConfig<T> { /** * The default value for the {@code maxTotal} configuration attribute. * @see GenericObjectPool#getMaxTotal() */ public static final int DEFAULT_MAX_TOTAL = 8; /** * The default value for the {@code maxIdle} configuration attribute. * @see GenericObjectPool#getMaxIdle() */ public static final int DEFAULT_MAX_IDLE = 8; /** * The default value for the {@code minIdle} configuration attribute. * @see GenericObjectPool#getMinIdle() */ public static final int DEFAULT_MIN_IDLE = 0; private int maxTotal = DEFAULT_MAX_TOTAL; private int maxIdle = DEFAULT_MAX_IDLE; private int minIdle = DEFAULT_MIN_IDLE;}GenericObjectPoolConfig继承了BaseObjectPoolConfig,其maxTotal为8,maxIdle为8,minIdle为0BaseObjectPoolConfigorg/apache/commons/pool2/impl/BaseObjectPoolConfig.java ...

September 24, 2023 · 4 min · jiezi

关于jedis:聊聊jedis的testWhileIdle

序本文次要钻研一下jedis的testWhileIdle testWhileIdleorg/apache/commons/pool2/impl/GenericObjectPool.java @Override public void evict() throws Exception { assertOpen(); if (!idleObjects.isEmpty()) { PooledObject<T> underTest = null; final EvictionPolicy<T> evictionPolicy = getEvictionPolicy(); synchronized (evictionLock) { final EvictionConfig evictionConfig = new EvictionConfig( getMinEvictableIdleDuration(), getSoftMinEvictableIdleDuration(), getMinIdle()); final boolean testWhileIdle = getTestWhileIdle(); for (int i = 0, m = getNumTests(); i < m; i++) { if (evictionIterator == null || !evictionIterator.hasNext()) { evictionIterator = new EvictionIterator(idleObjects); } if (!evictionIterator.hasNext()) { // Pool exhausted, nothing to do here return; } try { underTest = evictionIterator.next(); } catch (final NoSuchElementException nsee) { // Object was borrowed in another thread // Don't count this as an eviction test so reduce i; i--; evictionIterator = null; continue; } if (!underTest.startEvictionTest()) { // Object was borrowed in another thread // Don't count this as an eviction test so reduce i; i--; continue; } // User provided eviction policy could throw all sorts of // crazy exceptions. Protect against such an exception // killing the eviction thread. boolean evict; try { evict = evictionPolicy.evict(evictionConfig, underTest, idleObjects.size()); } catch (final Throwable t) { // Slightly convoluted as SwallowedExceptionListener // uses Exception rather than Throwable PoolUtils.checkRethrow(t); swallowException(new Exception(t)); // Don't evict on error conditions evict = false; } if (evict) { destroy(underTest, DestroyMode.NORMAL); destroyedByEvictorCount.incrementAndGet(); } else { if (testWhileIdle) { boolean active = false; try { factory.activateObject(underTest); active = true; } catch (final Exception e) { destroy(underTest, DestroyMode.NORMAL); destroyedByEvictorCount.incrementAndGet(); } if (active) { boolean validate = false; Throwable validationThrowable = null; try { validate = factory.validateObject(underTest); } catch (final Throwable t) { PoolUtils.checkRethrow(t); validationThrowable = t; } if (!validate) { destroy(underTest, DestroyMode.NORMAL); destroyedByEvictorCount.incrementAndGet(); if (validationThrowable != null) { if (validationThrowable instanceof RuntimeException) { throw (RuntimeException) validationThrowable; } throw (Error) validationThrowable; } } else { try { factory.passivateObject(underTest); } catch (final Exception e) { destroy(underTest, DestroyMode.NORMAL); destroyedByEvictorCount.incrementAndGet(); } } } } if (!underTest.endEvictionTest(idleObjects)) { // TODO - May need to add code here once additional // states are used } } } } } final AbandonedConfig ac = this.abandonedConfig; if (ac != null && ac.getRemoveAbandonedOnMaintenance()) { removeAbandoned(ac); } }GenericObjectPool的evict办法在idleObjects不为空的时候会执行evict逻辑,它先通过getNumTests获取每次要对多少个idleObject进行验证,之后循环解决,首先通过evictionPolicy.evict判断是否须要evict,如果是则执行destroy办法,否则判断是否testWhileIdle,若是则先执行activateObject办法,再执行validateObject,如果activateObject或者validateObject失败则执行destroy办法,如果validateObject胜利则执行passivateObject办法JedisFactoryredis/clients/jedis/JedisFactory.java ...

September 23, 2023 · 6 min · jiezi

关于jedis:聊聊jedis的return行为

序本文次要钻研一下jedis的return行为 spring-data-redisRedisTemplateorg/springframework/data/redis/core/RedisTemplate.java @Nullable public <T> T execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline) { Assert.isTrue(initialized, "template not initialized; call afterPropertiesSet() before using it"); Assert.notNull(action, "Callback object must not be null"); RedisConnectionFactory factory = getRequiredConnectionFactory(); RedisConnection conn = RedisConnectionUtils.getConnection(factory, enableTransactionSupport); try { boolean existingConnection = TransactionSynchronizationManager.hasResource(factory); RedisConnection connToUse = preProcessConnection(conn, existingConnection); boolean pipelineStatus = connToUse.isPipelined(); if (pipeline && !pipelineStatus) { connToUse.openPipeline(); } RedisConnection connToExpose = (exposeConnection ? connToUse : createRedisConnectionProxy(connToUse)); T result = action.doInRedis(connToExpose); // close pipeline if (pipeline && !pipelineStatus) { connToUse.closePipeline(); } return postProcessResult(result, connToUse, existingConnection); } finally { RedisConnectionUtils.releaseConnection(conn, factory, enableTransactionSupport); } }RedisTemplate的execute办法先通过RedisConnectionUtils.getConnection获取连贯,最初通过RedisConnectionUtils.releaseConnection来偿还连贯RedisConnectionUtilsorg/springframework/data/redis/core/RedisConnectionUtils.java ...

September 22, 2023 · 4 min · jiezi

关于jedis:聊聊jedis的borrow行为

序本文次要钻研一下jedis的borrow行为 borrowObjectorg/apache/commons/pool2/impl/GenericObjectPool.java public T borrowObject(final Duration borrowMaxWaitDuration) throws Exception { assertOpen(); final AbandonedConfig ac = this.abandonedConfig; if (ac != null && ac.getRemoveAbandonedOnBorrow() && (getNumIdle() < 2) && (getNumActive() > getMaxTotal() - 3)) { removeAbandoned(ac); } PooledObject<T> p = null; // Get local copy of current config so it is consistent for entire // method execution final boolean blockWhenExhausted = getBlockWhenExhausted(); boolean create; final long waitTimeMillis = System.currentTimeMillis(); while (p == null) { create = false; p = idleObjects.pollFirst(); if (p == null) { p = create(); if (p != null) { create = true; } } if (blockWhenExhausted) { if (p == null) { if (borrowMaxWaitDuration.isNegative()) { p = idleObjects.takeFirst(); } else { p = idleObjects.pollFirst(borrowMaxWaitDuration); } } if (p == null) { throw new NoSuchElementException(appendStats( "Timeout waiting for idle object, borrowMaxWaitDuration=" + borrowMaxWaitDuration)); } } else if (p == null) { throw new NoSuchElementException(appendStats("Pool exhausted")); } if (!p.allocate()) { p = null; } if (p != null) { try { factory.activateObject(p); } catch (final Exception e) { try { destroy(p, DestroyMode.NORMAL); } catch (final Exception e1) { // Ignore - activation failure is more important } p = null; if (create) { final NoSuchElementException nsee = new NoSuchElementException( appendStats("Unable to activate object")); nsee.initCause(e); throw nsee; } } if (p != null && getTestOnBorrow()) { boolean validate = false; Throwable validationThrowable = null; try { validate = factory.validateObject(p); } catch (final Throwable t) { PoolUtils.checkRethrow(t); validationThrowable = t; } if (!validate) { try { destroy(p, DestroyMode.NORMAL); destroyedByBorrowValidationCount.incrementAndGet(); } catch (final Exception e) { // Ignore - validation failure is more important } p = null; if (create) { final NoSuchElementException nsee = new NoSuchElementException( appendStats("Unable to validate object")); nsee.initCause(validationThrowable); throw nsee; } } } } } updateStatsBorrow(p, Duration.ofMillis(System.currentTimeMillis() - waitTimeMillis)); return p.getObject(); }borrowObject办法会开启一个while循环,条件是p为null,也就是要获取到p或者是外部本人跳出循环;idleObjects.pollFirst()从连接池获取,如果为null则执行create,之后是blockWhenExhausted的判断逻辑,如果create进去的为null,则阻塞期待takeFirst或者pollFirst(borrowMaxWaitDuration),如果还是null则抛出NoSuchElementException;如果blockWhenExhausted为false然而create为null则抛出Pool exhausted如果不是null,则再次确认下object的状态,如果变更状态(PooledObjectState.IDLE-->PooledObjectState.ALLOCATED)不胜利则返回null;接着执行factory.activateObject(p)办法,如果出现异常则destory掉(jedis这里只是在db不一样的时候会从新select,默认能够了解为空操作),紧接着是testOnBorrow的逻辑这里就是如果idleObjects.pollFirst()为null会触发create,如果还是null则间接抛出NoSuchElementException异样,跳出循环;只有在不为null且allocate失败的时候会重置为null持续循环;另外如果是create进去的然而activate不胜利也会抛出NoSuchElementException异样,跳出循环create /** * Attempts to create a new wrapped pooled object. * <p> * If there are {@link #getMaxTotal()} objects already in circulation * or in process of being created, this method returns null. * </p> * * @return The new wrapped pooled object * * @throws Exception if the object factory's {@code makeObject} fails */ private PooledObject<T> create() throws Exception { int localMaxTotal = getMaxTotal(); // This simplifies the code later in this method if (localMaxTotal < 0) { localMaxTotal = Integer.MAX_VALUE; } final long localStartTimeMillis = System.currentTimeMillis(); final long localMaxWaitTimeMillis = Math.max(getMaxWaitDuration().toMillis(), 0); // Flag that indicates if create should: // - TRUE: call the factory to create an object // - FALSE: return null // - null: loop and re-test the condition that determines whether to // call the factory Boolean create = null; while (create == null) { synchronized (makeObjectCountLock) { final long newCreateCount = createCount.incrementAndGet(); if (newCreateCount > localMaxTotal) { // The pool is currently at capacity or in the process of // making enough new objects to take it to capacity. createCount.decrementAndGet(); if (makeObjectCount == 0) { // There are no makeObject() calls in progress so the // pool is at capacity. Do not attempt to create a new // object. Return and wait for an object to be returned create = Boolean.FALSE; } else { // There are makeObject() calls in progress that might // bring the pool to capacity. Those calls might also // fail so wait until they complete and then re-test if // the pool is at capacity or not. makeObjectCountLock.wait(localMaxWaitTimeMillis); } } else { // The pool is not at capacity. Create a new object. makeObjectCount++; create = Boolean.TRUE; } } // Do not block more if maxWaitTimeMillis is set. if (create == null && (localMaxWaitTimeMillis > 0 && System.currentTimeMillis() - localStartTimeMillis >= localMaxWaitTimeMillis)) { create = Boolean.FALSE; } } if (!create.booleanValue()) { return null; } final PooledObject<T> p; try { p = factory.makeObject(); if (getTestOnCreate() && !factory.validateObject(p)) { createCount.decrementAndGet(); return null; } } catch (final Throwable e) { createCount.decrementAndGet(); throw e; } finally { synchronized (makeObjectCountLock) { makeObjectCount--; makeObjectCountLock.notifyAll(); } } final AbandonedConfig ac = this.abandonedConfig; if (ac != null && ac.getLogAbandoned()) { p.setLogAbandoned(true); p.setRequireFullStackTrace(ac.getRequireFullStackTrace()); } createdCount.incrementAndGet(); allObjects.put(new IdentityWrapper<>(p.getObject()), p); return p; }create办法不会判断createCount,如果超出则返回null,如果期待超出maxWait也会返回null;如果判断要创立则通过factory.makeObject(),另外针对testOnCreate且validateObject不通过的也返回null,如果是有异样则间接抛出makeObjectredis/clients/jedis/JedisFactory.java ...

September 21, 2023 · 5 min · jiezi