免费爱碰视频在线观看,九九精品国产屋,欧美亚洲尤物久久精品,1024在线观看视频亚洲

      如何優(yōu)雅的關(guān)閉 Java線程池

      簡(jiǎn)介 在開(kāi)發(fā)中使用線程池去執(zhí)行異步任務(wù)是比較普遍的操作,然而雖然有些異步操作我們并不十分要求可靠性和實(shí)時(shí)性,但總歸業(yè)務(wù)還是需要的。如果在每次的服務(wù)發(fā)版過(guò)程中,我們不去介入線程池的停機(jī)邏輯,那么很有可能就會(huì)造成線程池中隊(duì)列的任務(wù)還未執(zhí)行完成,自然就會(huì)造成數(shù)據(jù)的丟失。

      探究

      注意,本文所有前提是對(duì)進(jìn)程進(jìn)行下線時(shí)使用的是kill -15

      我們知道Spring已經(jīng)實(shí)現(xiàn)了自己的優(yōu)雅停機(jī)方案,詳細(xì)請(qǐng)參考o(jì)rg.springframework.context.support.AbstractApplicationContext#registerShutdownHook,然后主要看調(diào)用的org.springframework.context.support.AbstractApplicationContext#doClose, 在這個(gè)方法定義容器銷毀的執(zhí)行順序

      protected void doClose() { // Check whether an actual close attempt is necessary… if (this.active.get() && this.closed.compareAndSet(false, true)) { if (logger.isDebugEnabled()) { logger.debug(“Closing ” + this); } LiveBeansView.unregisterApplicationContext(this); try { // Publish shutdown event. publishEvent(new ContextClosedEvent(this)); } catch (Throwable ex) { logger.warn(“Exception thrown from ApplicationListener handling ContextClosedEvent”, ex); } // Stop all Lifecycle beans, to avoid delays during inpidual destruction. if (this.lifecycleProcessor != null) { try { this.lifecycleProcessor.onClose(); } catch (Throwable ex) { logger.warn(“Exception thrown from LifecycleProcessor on context close”, ex); } } // Destroy all cached singletons in the context’s BeanFactory. destroyBeans(); // Close the state of this context itself. closeBeanFactory(); // Let subclasses do some final clean-up if they wish… onClose(); // Reset local application listeners to pre-refresh state. if (this.earlyApplicationListeners != null) { this.applicationListeners.clear(); this.applicationListeners.addAll(this.earlyApplicationListeners); } // Switch to inactive. this.active.set(false); } }

      我們先主要關(guān)注下destroyBeans這個(gè)方法,看bean的銷毀邏輯是什么,然后看到了下面的一個(gè)bean的銷毀順序邏輯,具體方法在org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#destroySingletons

      private final Map disposableBeans = new LinkedHashMap(); public void destroySingletons() { if (logger.isTraceEnabled()) { logger.trace(“Destroying singletons in ” + this); } synchronized (this.singletonObjects) { this.singletonsCurrentlyInDestruction = true; } String[] disposableBeanNames; synchronized (this.disposableBeans) { disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet()); } for (int i = disposableBeanNames.length – 1; i >= 0; i–) { destroySingleton(disposableBeanNames[i]); } this.containedBeanMap.clear(); this.dependentBeanMap.clear(); this.dependenciesForBeanMap.clear(); clearSingletonCache(); }

      可以看到最至關(guān)重要的就是一個(gè)屬性disposableBeans,這個(gè)屬性是一個(gè)LinkedHashMap, 因此屬性是有序的,所以銷毀的時(shí)候也是按照某種規(guī)則保持和放入一樣的順序進(jìn)行銷毀的,現(xiàn)在就是要確認(rèn)這個(gè)屬性里到底存的是什么。

      經(jīng)過(guò)調(diào)試發(fā)現(xiàn),在創(chuàng)建bean的org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean方法中,會(huì)調(diào)用一個(gè)方法org.springframework.beans.factory.support.AbstractBeanFactory#registerDisposableBeanIfNecessary, 在這個(gè)方法中會(huì)調(diào)用org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#registerDisposableBean然后將當(dāng)前創(chuàng)建的bean放入到屬性disposableBeans中,那么現(xiàn)在來(lái)看一下放入的邏輯什么?

      相關(guān)代碼貼一下

      protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) { AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() : null); if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) { if (mbd.isSingleton()) { // Register a DisposableBean implementation that performs all destruction // work for the given bean: DestructionAwareBeanPostProcessors, // DisposableBean interface, custom destroy method. registerDisposableBean(beanName, new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc)); } else { // A bean with a custom scope… Scope scope = this.scopes.get(mbd.getScope()); if (scope == null) { throw new IllegalStateException(“No Scope registered for scope name ‘” + mbd.getScope() + “‘”); } scope.registerDestructionCallback(beanName, new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc)); } }}

      org.springframework.beans.factory.support.AbstractBeanFactory#requiresDestruction

      protected boolean requiresDestruction(Object bean, RootBeanDefinition mbd) { return (bean != null && (DisposableBeanAdapter.hasDestroyMethod(bean, mbd) || (hasDestructionAwareBeanPostProcessors() && DisposableBeanAdapter.hasApplicableProcessors(bean, getBeanPostProcessors())))); }

      經(jīng)過(guò)兩個(gè)方法可以看到如果一個(gè)bean的scope是singleton并且這個(gè)bean實(shí)現(xiàn)了org.springframework.beans.factory.DisposableBean這個(gè)接口的destroy()方法,那么就會(huì)滿足條件。

      現(xiàn)在可以確定一點(diǎn),如果我們將線程池交給Spring管理,并且實(shí)現(xiàn)它的close方法,就可以在應(yīng)用收到下線信號(hào)的時(shí)候執(zhí)行這個(gè)bean的銷毀方法,那么我們就可以在銷毀方法中寫(xiě)線程池的停機(jī)邏輯。

      我們知道Spring提供了線程池的封裝,在Spring中如果我們要定義線程池一般會(huì)使用org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor以及用于任務(wù)調(diào)度的org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler,先來(lái)簡(jiǎn)單看個(gè)定義ThreadPoolTaskExecutor線程池的例子

      @Configurationpublic class ThreadConfig { @Bean public ThreadPoolTaskExecutor testExecutor() { ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor(); threadPoolTaskExecutor.setThreadNamePrefix(“test-shutdown-pool-“); threadPoolTaskExecutor.setCorePoolSize(1); threadPoolTaskExecutor.setMaxPoolSize(1); threadPoolTaskExecutor.setKeepAliveSeconds(60); threadPoolTaskExecutor.setQueueCapacity(1000); threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy()); return threadPoolTaskExecutor; }}

      現(xiàn)在來(lái)一下線程池的這個(gè)類結(jié)構(gòu),ThreadPoolTaskExecutor繼承了org.springframework.scheduling.concurrent.ExecutorConfigurationSupport, 實(shí)現(xiàn)了org.springframework.beans.factory.DisposableBean,完整結(jié)構(gòu)如下

      public class ThreadPoolTaskExecutor extends ExecutorConfigurationSupport implements AsyncListenableTaskExecutor, SchedulingTaskExecutor { }public abstract class ExecutorConfigurationSupport extends CustomizableThreadFactory implements BeanNameAware, InitializingBean, DisposableBean {}

      從這里就能看到其實(shí)線程池類ThreadPoolTaskExecutor是滿足最開(kāi)始看到的銷毀條件的,那么現(xiàn)在就來(lái)看下在父類ExecutorConfigurationSupport中定義的destroy()方法,將其中關(guān)鍵部分代碼摘錄下來(lái)

      public abstract class ExecutorConfigurationSupport extends CustomizableThreadFactory implements BeanNameAware, InitializingBean, DisposableBean { private boolean waitForTasksToCompleteOnShutdown = false; private long awaitTerminationMillis = 0; @Nullable private ExecutorService executor; @Override public void destroy() { shutdown(); } /** * Perform a shutdown on the underlying ExecutorService. * @see java.util.concurrent.ExecutorService#shutdown() * @see java.util.concurrent.ExecutorService#shutdownNow() */ public void shutdown() { if (logger.isInfoEnabled()) { logger.info(“Shutting down ExecutorService” + (this.beanName != null ? ” ‘” + this.beanName + “‘” : “”)); } if (this.executor != null) { if (this.waitForTasksToCompleteOnShutdown) { this.executor.shutdown(); } else { for (Runnable remainingTask : this.executor.shutdownNow()) { cancelRemainingTask(remainingTask); } } awaitTerminationIfNecessary(this.executor); } } private void awaitTerminationIfNecessary(ExecutorService executor) { if (this.awaitTerminationMillis > 0) { try { if (!executor.awaitTermination(this.awaitTerminationMillis, TimeUnit.MILLISECONDS)) { if (logger.isWarnEnabled()) { logger.warn(“Timed out while waiting for executor” + (this.beanName != null ? ” ‘” + this.beanName + “‘” : “”) + ” to terminate”); } } } catch (InterruptedException ex) { if (logger.isWarnEnabled()) { logger.warn(“Interrupted while waiting for executor” + (this.beanName != null ? ” ‘” + this.beanName + “‘” : “”) + ” to terminate”); } Thread.currentThread().interrupt(); } } } protected void cancelRemainingTask(Runnable task) { if (task instanceof Future) { ((Future) task).cancel(true); } }}

      整個(gè)的邏輯還是比較清晰的, 在容器銷毀的時(shí)候會(huì)調(diào)用本地shutdown()方法, 在這個(gè)方法中會(huì)去判斷waitForTasksToCompleteOnShutdown這個(gè)的屬性,如果為true, 則調(diào)用線程池的shutdown()方法,這個(gè)方法并不會(huì)讓線程池立即停止,而是不再接受新的任務(wù)并繼續(xù)執(zhí)行已經(jīng)在隊(duì)列中的任務(wù)。如果為false, 則取消任務(wù)隊(duì)列中的剩余任務(wù)。而這個(gè)屬性的默認(rèn)值為false。因此默認(rèn)是不具備我們需要的功能的。

      然而無(wú)論這個(gè)值的屬性最終是否為T(mén)RUE,最終都會(huì)調(diào)用方法awaitTerminationIfNecessary(), 線程的停止無(wú)論是shutdown還是shutdownNow都無(wú)法保證線程池能夠停止下來(lái),因?yàn)樾枰浜暇€程池的方法awaitTermination使用,在這個(gè)方法中指定一個(gè)最大等待時(shí)間,則能夠保證線程池最終一定可以被停止下來(lái)。

      不知道有沒(méi)有注意到一個(gè)細(xì)節(jié),上述所有對(duì)線程池的操作使用的屬性都是private ExecutorService executor;,那么這個(gè)executor是什么時(shí)候賦值的呢?

      畢竟我們?cè)趧?chuàng)建bean的時(shí)候是直接new的ThreadPoolTaskExecutor,并沒(méi)有去處理這個(gè)屬性。還是看線程池的父類ExecutorConfigurationSupport, 其實(shí)現(xiàn)了接口org.springframework.beans.factory.InitializingBean,在容器初始化完成后有這樣一段代碼

      @Override public void afterPropertiesSet() { initialize(); } /** * Set up the ExecutorService. */ public void initialize() { if (logger.isInfoEnabled()) { logger.info(“Initializing ExecutorService” + (this.beanName != null ? ” ‘” + this.beanName + “‘” : “”)); } if (!this.threadNamePrefixSet && this.beanName != null) { setThreadNamePrefix(this.beanName + “-“); } this.executor = initializeExecutor(this.threadFactory, this.rejectedExecutionHandler); } protected abstract ExecutorService initializeExecutor( ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler);

      線程池bean在初始化完成后會(huì)調(diào)用父類的afterPropertiesSet方法,上面的代碼已經(jīng)很清晰的說(shuō)明了問(wèn)題, 最終父類中又定義了抽象方法initializeExecutor(),供子類去具體實(shí)現(xiàn)如果初始化這個(gè)屬性executor, 因?yàn)槲覀冎谰€程池的實(shí)現(xiàn)除了普通的異步任務(wù)線程池ThreadPoolTaskExecutor, 還有基于定時(shí)調(diào)度的線程池ThreadPoolTaskExecutor, 具體實(shí)現(xiàn)這里就不貼出來(lái)了,反正已經(jīng)能夠看出來(lái)這個(gè)屬性是如何被賦值的了,所以上述銷毀時(shí)代碼可以直接使用。

      現(xiàn)在整體總結(jié)下來(lái),其實(shí)發(fā)現(xiàn)我們Spring已經(jīng)幫我們實(shí)現(xiàn)了線程池的優(yōu)雅停機(jī)規(guī)則,在接收到停機(jī)信號(hào)時(shí),先拒絕接收新的任務(wù),并繼續(xù)執(zhí)行已經(jīng)接受的任務(wù),在任務(wù)執(zhí)行完成或者到達(dá)最大等待時(shí)間,完成線程池的關(guān)閉。這么一整套邏輯正是我們所需要的,而我們?nèi)绻褂眠@個(gè)邏輯,僅僅需要在配置線程池的時(shí)候指定下上面看到的waitForTasksToCompleteOnShutdown屬性和awaitTerminationMillis屬性。

      修改一下上面之前寫(xiě)的線程池定義代碼, 將waitForTasksToCompleteOnShutdown屬性設(shè)置為true, 并指定awaitTerminationMillis。

      @Configurationpublic class ThreadConfig { @Bean public ThreadPoolTaskExecutor testExecutor() { ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor(); threadPoolTaskExecutor.setThreadNamePrefix(“test-shutdown-pool-“); threadPoolTaskExecutor.setCorePoolSize(1); threadPoolTaskExecutor.setMaxPoolSize(1); threadPoolTaskExecutor.setKeepAliveSeconds(60); threadPoolTaskExecutor.setQueueCapacity(1000); threadPoolTaskExecutor.setWaitForTasksToCompleteOnShutdown(true); threadPoolTaskExecutor.setAwaitTerminationSeconds(60); threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy()); return threadPoolTaskExecutor; }}

      那么到目前這樣處理是不是就沒(méi)有問(wèn)題了呢?

      要分情況來(lái)看, 如果按照上述操作,是能夠保證最初預(yù)期目標(biāo)的。線程池會(huì)在接收到下線指令時(shí)停止接受新的任務(wù),并繼續(xù)執(zhí)行隊(duì)列中的未完成的任務(wù),直到任務(wù)完成或者達(dá)到指定的最大等待時(shí)間。

      如果任務(wù)是一些不操作其它資源的,只是一些本地計(jì)算或者日志什么之類的,那么任務(wù)不會(huì)出問(wèn)題。但是如果任務(wù)本身依賴各種數(shù)據(jù)源,比如數(shù)據(jù)庫(kù)、緩存等之類的,那么就會(huì)出現(xiàn)任務(wù)本身會(huì)執(zhí)行,但是卻會(huì)失敗的問(wèn)題,因?yàn)閿?shù)據(jù)源已經(jīng)早于線程池關(guān)閉了。

      那么,能不能控制數(shù)據(jù)源和線程池的銷毀順序呢?在上面我們看到銷毀順序的時(shí)候看到了線程池會(huì)在放入到disposableBeans的原因,其實(shí)數(shù)據(jù)源也是會(huì)被放入到這個(gè)屬性中的,這個(gè)原因和Spring的生命周期無(wú)關(guān),而是另外一個(gè)判斷條件。

      之前沒(méi)有貼出來(lái)具體代碼,現(xiàn)在來(lái)看下org.springframework.beans.factory.support.AbstractBeanFactory#requiresDestruction方法中的調(diào)用的另外一個(gè)本地方法org.springframework.beans.factory.support.DisposableBeanAdapter#hasDestroyMethod

      public static boolean hasDestroyMethod(Object bean, RootBeanDefinition beanDefinition) { if (bean instanceof DisposableBean || bean instanceof AutoCloseable) { return true; } String destroyMethodName = beanDefinition.getDestroyMethodName(); if (AbstractBeanDefinition.INFER_METHOD.equals(destroyMethodName)) { return (ClassUtils.hasMethod(bean.getClass(), CLOSE_METHOD_NAME) || ClassUtils.hasMethod(bean.getClass(), SHUTDOWN_METHOD_NAME)); } return StringUtils.hasLength(destroyMethodName); }

      之前線程池能夠執(zhí)行銷毀流程是因?yàn)閷?shí)現(xiàn)了接口DisposableBean, 而數(shù)據(jù)源則是實(shí)現(xiàn)了另外一個(gè)接口AutoCloseable, 因此數(shù)據(jù)源也是會(huì)執(zhí)行銷毀邏輯的。

      現(xiàn)在我們只要介入bean的創(chuàng)建優(yōu)先級(jí)即可, 使用org.springframework.core.annotation.Order注解來(lái)指定線程池創(chuàng)建的高優(yōu)先級(jí),如下。

      @Configuration@Order(value = Ordered.HIGHEST_PRECEDENCE – 10)public class ThreadConfig { @Bean @Order(value = Ordered.HIGHEST_PRECEDENCE – 10) public ThreadPoolTaskExecutor testExecutor() { ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor(); threadPoolTaskExecutor.setThreadNamePrefix(“test-shutdown-pool-“); threadPoolTaskExecutor.setCorePoolSize(1); threadPoolTaskExecutor.setMaxPoolSize(1); threadPoolTaskExecutor.setKeepAliveSeconds(60); threadPoolTaskExecutor.setQueueCapacity(2000000); threadPoolTaskExecutor.setWaitForTasksToCompleteOnShutdown(true); threadPoolTaskExecutor.setAwaitTerminationSeconds(60); threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy()); return threadPoolTaskExecutor; }}

      當(dāng)然實(shí)際上的優(yōu)先級(jí)要根據(jù)情況調(diào)整,但是并沒(méi)有生效。后來(lái)看到一個(gè)說(shuō)法,org.springframework.core.annotation.Order注解無(wú)法決定bean的創(chuàng)建順序,只能是bean創(chuàng)建完成后的一些業(yè)務(wù)的執(zhí)行時(shí)間。這個(gè)問(wèn)題沒(méi)搞懂,反正Order未生效。那么只能抄他的代碼然后自己實(shí)現(xiàn)了。

      該如何處理呢?

      自實(shí)現(xiàn)

      回到我們最初的代碼org.springframework.context.support.AbstractApplicationContext#doClose, 之前我們一直在看銷毀bean的邏輯,但是其實(shí)我們可以看到在此之前Spring發(fā)布了一個(gè)ContextClosedEvent事件,也就是說(shuō)這個(gè)事件是早于Spring自己的bean銷毀邏輯前面的。

      利用這個(gè)機(jī)制,我們可以將線程池的銷毀邏輯抄過(guò)來(lái),并且監(jiān)聽(tīng)ContextClosedEvent這個(gè)事件,然后在這個(gè)事件里執(zhí)行我們本地自己一些不被Spring管理的線程池的銷毀邏輯,正如前面看到的一樣。

      一個(gè)簡(jiǎn)單的例子如下

      @Component@Slf4jpublic class ThreadPoolExecutorShutdownDefinition implements ApplicationListener { private static final List POOLS = Collections.synchronizedList(new ArrayList(12)); /** * 線程中的任務(wù)在接收到應(yīng)用關(guān)閉信號(hào)量后最多等待多久就強(qiáng)制終止,其實(shí)就是給剩余任務(wù)預(yù)留的時(shí)間, 到時(shí)間后線程池必須銷毀 */ private static final long awaitTermination = 60; /** * awaitTermination的單位 */ private static final TimeUnit timeUnit = TimeUnit.SECONDS; /** * 注冊(cè)要關(guān)閉的線程池 * 注意如果調(diào)用這個(gè)方法的話,而線程池又是由Spring管理的,則必須等待這個(gè)bean初始化完成后才可以調(diào)用 * 因?yàn)橐蕾嚨膡@link ThreadPoolTaskExecutor#getThreadPoolExecutor()}必須要在bean的父類方法中定義的 * 初始化{@link ExecutorConfigurationSupport#afterPropertiesSet()}方法中才會(huì)賦值 * * @param threadPoolTaskExecutor */ public static void registryExecutor(ThreadPoolTaskExecutor threadPoolTaskExecutor) { POOLS.add(threadPoolTaskExecutor.getThreadPoolExecutor()); } /** * 注冊(cè)要關(guān)閉的線程池 * 注意如果調(diào)用這個(gè)方法的話,而線程池又是由Spring管理的,則必須等待這個(gè)bean初始化完成后才可以調(diào)用 * 因?yàn)橐蕾嚨膡@link ThreadPoolTaskExecutor#getThreadPoolExecutor()}必須要在bean的父類方法中定義的 * 初始化{@link ExecutorConfigurationSupport#afterPropertiesSet()}方法中才會(huì)賦值 * * 重寫(xiě)了{(lán)@link ThreadPoolTaskScheduler#initializeExecutor(java.util.concurrent.ThreadFactory, java.util.concurrent.RejectedExecutionHandler)} * 來(lái)對(duì)父類的{@link ExecutorConfigurationSupport#executor}賦值 * * @param threadPoolTaskExecutor */ public static void registryExecutor(ThreadPoolTaskScheduler threadPoolTaskExecutor) { POOLS.add(threadPoolTaskExecutor.getScheduledThreadPoolExecutor()); } /** * 注冊(cè)要關(guān)閉的線程池, 如果一些線程池未交由線程池管理,則可以調(diào)用這個(gè)方法 * * @param executor */ public static void registryExecutor(ExecutorService executor) { POOLS.add(executor); } /** * 參考{@link org.springframework.scheduling.concurrent.ExecutorConfigurationSupport#shutdown()} * * @param event the event to respond to */ @Override public void onApplicationEvent(ContextClosedEvent event) { log.info(“容器關(guān)閉前處理線程池優(yōu)雅關(guān)閉開(kāi)始, 當(dāng)前要處理的線程池?cái)?shù)量為: {} >>>>>>>>>>>>>>>>”, POOLS.size()); if (CollectionUtils.isEmpty(POOLS)) { return; } for (ExecutorService pool : POOLS) { pool.shutdown(); try { if (!pool.awaitTermination(awaitTermination, timeUnit)) { if (log.isWarnEnabled()) { log.warn(“Timed out while waiting for executor [{}] to terminate”, pool); } } } catch (InterruptedException ex) { if (log.isWarnEnabled()) { log.warn(“Timed out while waiting for executor [{}] to terminate”, pool); } Thread.currentThread().interrupt(); } } }}

      如果想要本地的線程池實(shí)現(xiàn)優(yōu)雅停機(jī),則直接調(diào)用上述對(duì)應(yīng)的registryExecutor()方法即可,在容器銷毀的時(shí)候自然會(huì)去遍歷執(zhí)行對(duì)應(yīng)邏輯。

      做一點(diǎn)補(bǔ)充

      我們所謂的優(yōu)雅停機(jī), 必然是需要各方面的一些配合的。因?yàn)橐粋€(gè)應(yīng)用總歸最重要的還是外界的流量,上面只是處理了線程池的問(wèn)題。

      如果是普通的springboot項(xiàng)目, 停機(jī)無(wú)法解決流量繼續(xù)轉(zhuǎn)發(fā)進(jìn)來(lái)的問(wèn)題, 如nginx,只要保證發(fā)布時(shí)發(fā)送kill -15的信號(hào)量并且將發(fā)布機(jī)器從nginx負(fù)載中下線。

      如果是Dubbo項(xiàng)目,則麻煩一些, 問(wèn)題其實(shí)和上述類似,由于Dubbo也注冊(cè)了關(guān)閉的鉤子, 則在停機(jī)時(shí)會(huì)同時(shí)存在多個(gè)鉤子要執(zhí)行的問(wèn)題。如果Spring的一些容器先銷毀了,Dubbo中的一些任務(wù)則無(wú)法繼續(xù)執(zhí)行。

      java.lang.Runtime#addShutdownHook, 當(dāng)存在多個(gè)注冊(cè)的關(guān)閉鉤子時(shí),虛擬機(jī)會(huì)以某種未指定的順序并讓它們同時(shí)運(yùn)行。這就是上述問(wèn)題存在的原因。

      public void addShutdownHook(Thread hook) { SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(new RuntimePermission(“shutdownHooks”)); } ApplicationShutdownHooks.add(hook);}

      Dubbo應(yīng)用則和上面手動(dòng)監(jiān)聽(tīng)容器銷毀事件的原理類似, 要讓Dubbo的鉤子先執(zhí)行,由于Dubbo已經(jīng)自己注冊(cè)了關(guān)閉鉤子,那么我們的步驟就變成了在Sprign容器啟動(dòng)的時(shí)候先移除掉Dubbo的shutdownHook, 然后再容器銷毀的時(shí)候再添加回來(lái)。

      綜合上面線程池的邏輯, 我們還要保證添加Dubbo的shutdownhook的Listener先執(zhí)行并且執(zhí)行完它的停機(jī)邏輯之后才執(zhí)行我們自己寫(xiě)的處理線程池停機(jī)的Listener,當(dāng)然如果線程池全部交由了Spring管理,自己沒(méi)有按照上面去重寫(xiě)這一塊的邏輯, 則不需要注意這個(gè)問(wèn)題。

      移除和添加Dubbo的shutdownHook的邏輯類似如下.

      public class DubboShutdownListener implements ApplicationListener, PriorityOrdered { private static final Logger LOGGER = LoggerFactory.getLogger(DubboShutdownListener.class); public DubboShutdownListener() { } public void onApplicationEvent(ApplicationEvent event) { if (event instanceof ApplicationStartedEvent) { Runtime.getRuntime().removeShutdownHook(DubboShutdownHook.getDubboShutdownHook()); LOGGER.info(“Dubbo default shutdown hook has been removed, It will be managed by Spring”); } else if (event instanceof ContextClosedEvent) { LOGGER.info(“Start destroy Dubbo Container on Spring close event”); DubboShutdownHook.getDubboShutdownHook().destroyAll(); LOGGER.info(“Dubbo Container has been destroyed finished”); } } public int getOrder() { return 0; }}

      鄭重聲明:本文內(nèi)容及圖片均整理自互聯(lián)網(wǎng),不代表本站立場(chǎng),版權(quán)歸原作者所有,如有侵權(quán)請(qǐng)聯(lián)系管理員(admin#wlmqw.com)刪除。
      上一篇 2022年8月30日 15:07
      下一篇 2022年8月30日 15:07

      相關(guān)推薦

      聯(lián)系我們

      聯(lián)系郵箱:admin#wlmqw.com
      工作時(shí)間:周一至周五,10:30-18:30,節(jié)假日休息