jedis的testWhileIdle用法源碼解讀
序
本文主要研究一下jedis的testWhileIdle
testWhileIdle
org/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不為空的時(shí)候會執(zhí)行evict邏輯,它先通過getNumTests獲取每次要對多少個(gè)idleObject進(jìn)行驗(yàn)證,之后循環(huán)處理,首先通過evictionPolicy.evict判斷是否需要evict,如果是則執(zhí)行destroy方法,否則判斷是否testWhileIdle,若是則先執(zhí)行activateObject方法,再執(zhí)行validateObject,如果activateObject或者validateObject失敗則執(zhí)行destroy方法,如果validateObject成功則執(zhí)行passivateObject方法
JedisFactory
redis/clients/jedis/JedisFactory.java
@Override public void activateObject(PooledObject<Jedis> pooledJedis) throws Exception { final BinaryJedis jedis = pooledJedis.getObject(); if (jedis.getDB() != clientConfig.getDatabase()) { jedis.select(clientConfig.getDatabase()); } } @Override public boolean validateObject(PooledObject<Jedis> pooledJedis) { final BinaryJedis jedis = pooledJedis.getObject(); try { String host = jedisSocketFactory.getHost(); int port = jedisSocketFactory.getPort(); String connectionHost = jedis.getClient().getHost(); int connectionPort = jedis.getClient().getPort(); return host.equals(connectionHost) && port == connectionPort && jedis.isConnected() && jedis.ping().equals("PONG"); } catch (final Exception e) { logger.error("Error while validating pooled Jedis object.", e); return false; } } @Override public void passivateObject(PooledObject<Jedis> pooledJedis) throws Exception { // TODO maybe should select db 0? Not sure right now. } @Override public void destroyObject(PooledObject<Jedis> pooledJedis) throws Exception { final BinaryJedis jedis = pooledJedis.getObject(); if (jedis.isConnected()) { try { // need a proper test, probably with mock if (!jedis.isBroken()) { jedis.quit(); } } catch (RuntimeException e) { logger.warn("Error while QUIT", e); } try { jedis.close(); } catch (RuntimeException e) { logger.warn("Error while close", e); } } }
JedisFactory的activateObject判斷db是否一樣,不一樣則執(zhí)行select方法;validateObject方法則執(zhí)行ping;passivateObject方法為空操作;destroyObject方法會判斷是否broken,非broken執(zhí)行quit,最后執(zhí)行close方法
Evictor
org/apache/commons/pool2/impl/BaseGenericObjectPool.java
/** * The idle object evictor {@link TimerTask}. * * @see GenericKeyedObjectPool#setTimeBetweenEvictionRunsMillis */ class Evictor implements Runnable { private ScheduledFuture<?> scheduledFuture; /** * Cancels the scheduled future. */ void cancel() { scheduledFuture.cancel(false); } /** * Run pool maintenance. Evict objects qualifying for eviction and then * ensure that the minimum number of idle instances are available. * Since the Timer that invokes Evictors is shared for all Pools but * pools may exist in different class loaders, the Evictor ensures that * any actions taken are under the class loader of the factory * associated with the pool. */ @Override public void run() { final ClassLoader savedClassLoader = Thread.currentThread().getContextClassLoader(); try { if (factoryClassLoader != null) { // Set the class loader for the factory final ClassLoader cl = factoryClassLoader.get(); if (cl == null) { // The pool has been dereferenced and the class loader // GC'd. Cancel this timer so the pool can be GC'd as // well. cancel(); return; } Thread.currentThread().setContextClassLoader(cl); } // Evict from the pool try { evict(); } catch(final Exception e) { swallowException(e); } catch(final OutOfMemoryError oome) { // Log problem but give evictor thread a chance to continue // in case error is recoverable oome.printStackTrace(System.err); } // Re-create idle instances. try { ensureMinIdle(); } catch (final Exception e) { swallowException(e); } } finally { // Restore the previous CCL Thread.currentThread().setContextClassLoader(savedClassLoader); } } /** * Sets the scheduled future. * * @param scheduledFuture the scheduled future. */ void setScheduledFuture(final ScheduledFuture<?> scheduledFuture) { this.scheduledFuture = scheduledFuture; } }
Evictor實(shí)現(xiàn)了Runnable方法,其run方法先執(zhí)行evict方法,后執(zhí)行ensureMinIdle方法
setTimeBetweenEvictionRuns
org/apache/commons/pool2/impl/BaseGenericObjectPool.java
/** * Sets the number of milliseconds to sleep between runs of the idle object evictor thread. * <ul> * <li>When positive, the idle object evictor thread starts.</li> * <li>When non-positive, no idle object evictor thread runs.</li> * </ul> * * @param timeBetweenEvictionRuns * duration to sleep between evictor runs * * @see #getTimeBetweenEvictionRunsMillis * @since 2.10.0 */ public final void setTimeBetweenEvictionRuns(final Duration timeBetweenEvictionRuns) { this.durationBetweenEvictionRuns = PoolImplUtils.nonNull(timeBetweenEvictionRuns, BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS); startEvictor(this.durationBetweenEvictionRuns); }
setTimeBetweenEvictionRuns方法會給durationBetweenEvictionRuns賦值,同時(shí)執(zhí)行startEvictor方法
startEvictor
org/apache/commons/pool2/impl/BaseGenericObjectPool.java
/** * <p>Starts the evictor with the given delay. If there is an evictor * running when this method is called, it is stopped and replaced with a * new evictor with the specified delay.</p> * * <p>This method needs to be final, since it is called from a constructor. * See POOL-195.</p> * * @param delay time in milliseconds before start and between eviction runs */ final void startEvictor(final Duration delay) { synchronized (evictionLock) { final boolean isPositiverDelay = PoolImplUtils.isPositive(delay); if (evictor == null) { // Starting evictor for the first time or after a cancel if (isPositiverDelay) { // Starting new evictor evictor = new Evictor(); EvictionTimer.schedule(evictor, delay, delay); } } else if (isPositiverDelay) { // Stop or restart of existing evictor: Restart synchronized (EvictionTimer.class) { // Ensure no cancel can happen between cancel / schedule calls EvictionTimer.cancel(evictor, evictorShutdownTimeoutDuration, true); evictor = null; evictionIterator = null; evictor = new Evictor(); EvictionTimer.schedule(evictor, delay, delay); } } else { // Stopping evictor EvictionTimer.cancel(evictor, evictorShutdownTimeoutDuration, false); } } }
startEvictor方法會判斷delay是否是正數(shù),是的話,則執(zhí)行EvictionTimer.schedule(evictor, delay, delay),不是則執(zhí)行EvictionTimer.cancel(evictor, evictorShutdownTimeoutDuration, false);對于evictor不為null的會先執(zhí)行cancel再執(zhí)行schedule
EvictionTimer
org/apache/commons/pool2/impl/EvictionTimer.java
/** * Adds the specified eviction task to the timer. Tasks that are added with * a call to this method *must* call {@link * #cancel(BaseGenericObjectPool.Evictor, Duration, boolean)} * to cancel the task to prevent memory and/or thread leaks in application * server environments. * * @param task Task to be scheduled. * @param delay Delay in milliseconds before task is executed. * @param period Time in milliseconds between executions. */ static synchronized void schedule( final BaseGenericObjectPool<?>.Evictor task, final Duration delay, final Duration period) { if (null == executor) { executor = new ScheduledThreadPoolExecutor(1, new EvictorThreadFactory()); executor.setRemoveOnCancelPolicy(true); executor.scheduleAtFixedRate(new Reaper(), delay.toMillis(), period.toMillis(), TimeUnit.MILLISECONDS); } final WeakReference<Runnable> ref = new WeakReference<>(task); final WeakRunner runner = new WeakRunner(ref); final ScheduledFuture<?> scheduledFuture = executor.scheduleWithFixedDelay(runner, delay.toMillis(), period.toMillis(), TimeUnit.MILLISECONDS); task.setScheduledFuture(scheduledFuture); taskMap.put(ref, runner); }
schedule方法使用的是ScheduledThreadPoolExecutor的scheduleWithFixedDelay方法來執(zhí)行evictor;而再executor為null時(shí)會創(chuàng)建ScheduledThreadPoolExecutor,同時(shí)觸發(fā)scheduleAtFixedRate來執(zhí)行Reaper
Reaper
org/apache/commons/pool2/impl/EvictionTimer.java
/** * Task that removes references to abandoned tasks and shuts * down the executor if there are no live tasks left. */ private static class Reaper implements Runnable { @Override public void run() { synchronized (EvictionTimer.class) { for (final Entry<WeakReference<Runnable>, WeakRunner> entry : taskMap.entrySet()) { if (entry.getKey().get() == null) { executor.remove(entry.getValue()); taskMap.remove(entry.getKey()); } } if (taskMap.isEmpty() && executor != null) { executor.shutdown(); executor.setCorePoolSize(0); executor = null; } } } }
Reaper主要是遍歷taskMap,刪除被cancel掉的task
小結(jié)
jedis的testWhileIdle是依賴Evictor來進(jìn)行的,即Evictor它通過evictionPolicy.evict判斷是否需要evict,如果是則執(zhí)行evict邏輯,即destroy方法,否則走testWhileIdle的邏輯。testWhileIdle先執(zhí)行activateObject方法,再執(zhí)行validateObject,如果activateObject或者validateObject失敗則執(zhí)行destroy方法,最后如果validateObject成功則執(zhí)行passivateObject方法。
Evictor實(shí)現(xiàn)了Runnable方法,其run方法先執(zhí)行evict方法,后執(zhí)行ensureMinIdle方法;BaseGenericObjectPool的setTimeBetweenEvictionRuns方法會給durationBetweenEvictionRuns賦值,同時(shí)執(zhí)行startEvictor方法,即觸發(fā)執(zhí)行EvictionTimer.schedule(evictor, delay, delay),schedule方法使用的是ScheduledThreadPoolExecutor的scheduleWithFixedDelay方法來執(zhí)行evictor。
doc
以上就是jedis的testWhileIdle的詳細(xì)內(nèi)容,更多關(guān)于jedis的testWhileIdle的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringBoot中@ConditionalOnProperty的使用及作用詳解
這篇文章主要介紹了SpringBoot中@ConditionalOnProperty的使用及作用詳解,@ConditionalOnProperty通過讀取本地配置文件中的值來判斷 某些 Bean 或者 配置類 是否加入spring 中,需要的朋友可以參考下2024-01-01Java Properties簡介_動力節(jié)點(diǎn)Java學(xué)院整理
Java中有個(gè)比較重要的類Properties(Java.util.Properties),主要用于讀取Java的配置文件,各種語言都有自己所支持的配置文件,配置文件中很多變量是經(jīng)常改變的,這樣做也是為了方便用戶,讓用戶能夠脫離程序本身去修改相關(guān)的變量設(shè)置2017-05-05Java中的List接口實(shí)現(xiàn)類LinkList和ArrayList詳解
這篇文章主要介紹了Java中的List接口實(shí)現(xiàn)類LinkList和ArrayList詳解,List接口繼承自Collection接口,是單列集合的一個(gè)重要分支,實(shí)現(xiàn)了List接口的對象稱為List集合,在List集合中允許出現(xiàn)重復(fù)的元素,所有的元素是以一種線性方式進(jìn)行存儲的,需要的朋友可以參考下2024-01-01Java使用組件編寫窗口實(shí)現(xiàn)網(wǎng)絡(luò)圖片顯示
這篇文章主要為大家詳細(xì)介紹了Java使用組件編寫窗口實(shí)現(xiàn)網(wǎng)絡(luò)圖片顯示的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-02-02Java實(shí)現(xiàn)斷點(diǎn)續(xù)傳功能的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何利用Java語言實(shí)現(xiàn)網(wǎng)絡(luò)資源的斷點(diǎn)續(xù)傳功能,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的可以了解一下2022-10-10SpringMVC如何自定義響應(yīng)的HTTP狀態(tài)碼
這篇文章主要介紹了SpringMVC如何自定義響應(yīng)的HTTP狀態(tài)碼,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11