Java程序优雅的退出

背景

最近收到一个新需求,需要充kafka队列中那消息解析写到es中。要求不能漏写数据,或者重复写数据。‘

问题

如果程序中途需要手动停止,就需要把已经从kafka中拿到的数据,写进了es 才能停止程序。否则就会漏写数据或者重复写入数据

解决

钩子函数

ShutdownHook只是一个已初始化但为启动的线程。当JVM开始执行关闭序列时,它才开始已某种随机程序注册和并行执行shutdown hooks。

注意 这个对 kill -9 {pid} 无效,对 kill -15 及 Control +C 有效

1
2
3
4
5
6
7
8
9
10
11
12
13
//注册钩子函数
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
log.info("开始停止任务");
//停止标识位 为 true 停止从kafka 拿数据
isStop = true;
try {
//阻塞 直到将已经拿到的数据处理完毕
executorService.awaitTermination(1, TimeUnit.HOURS);
} catch (Exception e) {
e.printStackTrace();
}
log.info("任务已经停止");
}));

SignalHandler

实现sun.misc.SignalHandler 接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class SignalHandlerImp implements SignalHandler {

public void handle(Signal signal) {
//停止标识位 为 true 停止从kafka 拿数据
isStop = true;
try {
//阻塞 直到将已经拿到的数据处理完毕
executorService.awaitTermination(1, TimeUnit.HOURS);
} catch (Exception e) {
e.printStackTrace();
}
log.info("任务已经停止");
System.out.println(signal.getName()+":"+signal.getNumber());
}

}
1
2
3
4
5
6

// 注册要监听的信号
SignalHandlerImp signalHandlerImp = new SignalHandlerImp();
Signal.handle(new Signal("INT"), signalHandlerImp);// 2 : 中断(同 ctrl + c )
Signal.handle(new Signal("TERM"), signalHandlerImp);// 15 : 正常终止
Signal.handle(new Signal("USR2"), signalHandlerImp);// 12 : 用户自定义信号

总结

jvm 关闭的几种方式

jvm关闭

我们可以通过 钩子函数来 处理 正常关闭以及异常关闭的收尾工作。SignalHandler 只能处理正常关闭的收尾工作。所以钩子函数的应用要广泛一点的。