- 使用actuator,通過發送http請求關閉
- 將應用注冊為linux服務,通過service xxx stop關閉
具體這兩種方式如何實現,這里就不說了,網上百度一堆,主要講一下在這兩種情況下web應用listener的一些問題
一般來講,我們的應用都應該在結束的時候對資源進行回收處理,jvm幫我們做了一部分,springboot也做了相應bean的回收,那如果是我們自己創建的線程池或是其他未托管於spring的資源呢?
-
在非web的應用中,我們一般使用勾子來實現,從而保證在jvm退出時,能夠進行一些資源的回收(直接kill無法保證執行),kill命令相當於直接把應用干掉,是一種非正常情況下中止應用的方式。ctrl+c,System.exit(),程序正常退出都會觸發勾子
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { @Override public void run() { try { shutdown(componentName); } catch (Exception e) { LOGGER.error("shutdown error:", e); }} }, componentName + <span class="hljs-string">"_shutdown_hook_thread"</span>));</pre> </li> -
在web應用中,勾子就不一定能夠生效了,而是通過實現ServletContextListener接口,可以在context初始化和結束的時候做一些資源創建和回收的操作
public class ContextListener implements ServletContextListener { private static final Logger LOGGER = LoggerFactory.getLogger(ContextListener.class);<span class="hljs-annotation">@Override</span> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">contextInitialized</span><span class="hljs-params">(ServletContextEvent servletContextEvent)</span> </span>{ LOGGER.info(<span class="hljs-string">"contextInitialized"</span>); } <span class="hljs-annotation">@Override</span> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">contextDestroyed</span><span class="hljs-params">(ServletContextEvent servletContextEvent)</span> </span>{ LOGGER.info(<span class="hljs-string">"contextDestroyed begin..."</span>); ShutdownUtil.destroy(); LOGGER.info(<span class="hljs-string">"contextDestroyed end..."</span>); } }</pre> </li>
問題來了,在使用第二種方式將springboot應用注冊為linux服務時,發現通過service xxx stop命令停止應用時,contextDestroyed的日志根本沒打印出來,懷疑是LOGGER對象已經被jvm回收,於是改成System.out,這次begin打印出來了,但是並沒有調用 ShutdownUtil.destroy()方法,懷疑同上,具體的機制沒去詳細了解,猜測是springboot自己的回收策略,或者是該插件的坑。
使用actuator的方式沒有問題,會正常的執行destroyed的回收
