Java 如何利用钩子函数实现优雅停服?刨根问底

在 JAVA 的世界里遨游,如果能拥有一双善于发现的眼睛,有很多东西留心去看,外加耐心助力,仔细去品,往往会品出不一样的味道 。
【Java 如何利用钩子函数实现优雅停服?刨根问底】通过本次分享,能让你轻松 get 如下几点,绝对收获满满 。

a)如何让 Java 程序实现优雅停服?有思想才是硬道理!
 
b)addShutdownHook 的使用场景?会用才是王道!
 
c)addShutdownHook 钩子函数到底是个啥?刨根问底!
1. 如何让 Java 程序实现优雅停服?无论是自研基础服务框架,还是分析开源项目源码,细心的 Java 开发同学,都会发现 Runtime.getRuntime().addShutdownHook 这么一句代码的身影,这句到底是干什么用的?
接下来就一起细品,看看它香不香?
阿里开源的数据同步神器 Canal 启动时的部分源码:
Java 如何利用钩子函数实现优雅停服?刨根问底

文章插图
 
Apache 麾下的用于海量日志收集的 Flume 启动时的部分源码:
Java 如何利用钩子函数实现优雅停服?刨根问底

文章插图
 
仰望了一下开源的项目,不妨从中提炼一下共性(同样的代码遇到多次,势必会品出味道),写段代码跑跑看(站在 flume 源码的肩膀上,起飞) 。
import java.util.concurrent.ScheduledThreadPoolExecutor;import java.util.concurrent.TimeUnit;/** * 体验 Java 优雅停服 * * @author 一猿小讲 */public class Application {/*** 监控服务*/private ScheduledThreadPoolExecutor monitorService;public Application() {monitorService = new ScheduledThreadPoolExecutor(1);}/*** 启动监控服务,监控一下内存信息*/public void start() {System.out.println(String.format("启动监控服务 %s", Thread.currentThread().getId()));monitorService.scheduleWithFixedDelay(new Runnable() {@Overridepublic void run() {System.out.println(String.format("最大内存: %dm已分配内存: %dm已分配内存中的剩余空间: %dm最大可用内存: %dm",Runtime.getRuntime().maxMemory() / 1024 / 1024,Runtime.getRuntime().totalMemory() / 1024 / 1024,Runtime.getRuntime().freeMemory() / 1024 / 1024,(Runtime.getRuntime().maxMemory() - Runtime.getRuntime().totalMemory() +Runtime.getRuntime().freeMemory()) / 1024 / 1024));}}, 2, 2, TimeUnit.SECONDS);}/*** 释放资源(代码来源于 flume 源码)* 主要用于关闭线程池(看不懂的同学莫纠结,当做黑盒去对待)*/public void stop() {System.out.println(String.format("开始关闭线程池 %s", Thread.currentThread().getId()));if (monitorService != null) {monitorService.shutdown();try {monitorService.awaitTermination(10, TimeUnit.SECONDS);} catch (InterruptedException e) {System.err.println("Interrupted while waiting for monitor service to stop");}if (!monitorService.isTerminated()) {monitorService.shutdownNow();try {while (!monitorService.isTerminated()) {monitorService.awaitTermination(10, TimeUnit.SECONDS);}} catch (InterruptedException e) {System.err.println("Interrupted while waiting for monitor service to stop");}}}System.out.println(String.format("线程池关闭完成 %s", Thread.currentThread().getId()));}/*** 应用入口*/public static void main(String[] args) {Application application = new Application();// 启动服务(每隔一段时间监控输出一下内存信息)application.start();// 添加钩子,实现优雅停服(主要验证钩子的作用)final Application appReference = application;Runtime.getRuntime().addShutdownHook(new Thread("shutdown-hook") {@Overridepublic void run() {System.out.println("接收到退出的讯号,开始打扫战场,释放资源,完成优雅停服");appReference.stop();}});System.out.println("服务启动完成");}}经常读文的我很清楚,耐心读文章中源码的同学应该很少,所以我还是用图给你简单捋一捋 。
Java 如何利用钩子函数实现优雅停服?刨根问底

文章插图
 
标注1:start 方法利用线程池启动一个线程去定时监控内存信息;
标注2:stop 方法用于在退出程序之前,进行关闭线程池进而释放资源 。
程序跑起来,效果如下 。
Java 如何利用钩子函数实现优雅停服?刨根问底

文章插图
 
当进行 kill 操作时,程序确实进行了资源释放,效果确实很优雅 。
Java 如何利用钩子函数实现优雅停服?刨根问底

文章插图
 
一切看似那么自然,一切又是那么完美,这是真的吗?杀进程时候如果用 kill -9,这种情况下会发生什么现象呢?
Java 如何利用钩子函数实现优雅停服?刨根问底

文章插图


推荐阅读