1、优雅的姿态是什么样
当我们触发关闭的操作时,程序接收到关闭进程的指令,程序在关闭之前需要完成以下步骤:
- 停止接收客户端的连接
- 执行线程池中未执行完毕的任务
- 持久化缓存中的数据
- 清理临时的文件等
这就是我们对于优雅安全的关闭游戏服的目标,按照以上顺序关闭服务器不会造成数据的丢失。
2、如何进行处理
JVM 提供了关闭钩子用来满足以上的需求。关闭钩子实际上是一种 hook 线程,用来监听 JVM 的关闭,当然,该 hook 线程只有在 JVM 正常关闭的情况下才会触发相关操作,强制关闭下无法执行 hook 操作,主要的触发情形有以下几种:
- 程序正常退出
- 调用 system.exit()方法
- 使用 ctrl+c 命令中断
- 系统关闭
- OutOfMemory 引起的程序退出
- 使用 kill pid 命令中断
注意:kill -9 pid 命令无法触发 shutdown hook 执行,该命令属于强制关闭
3、如何使用钩子
简单使用实例:
public class Test {
public static void main(String[] args) {
System.out.println("main exec...");
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override
public void run() {
System.out.println("监听到JVM关闭操作,执行hook的相关操作...");
}
}));
}
}
对于一个完整的项目来说,关闭时往往需要注册多个关闭钩子进行处理,这些钩子会并发执行操作,JVM 并不能控制其执行的顺序,当钩子执行时 JVM 延迟关闭,所有的钩子操作执行完毕后 JVM 才会退出,所以我们应当尽量减少 hook 操作的时间。另外钩子并发执行的特点,很容易出现死锁和竞态条件等问题,原则上我们应当在一个钩子中执行一系列的相关操作。
以下还有一些需要注意的地方:
- hook 线程中抛出的异常需要进行处理,否则会中断执行
- hook 执行逻辑中不能调用 system.exit()方法,否则会造成死循环
- system.exit()方法后注册的钩子无法执行
- 不能再 hook 中进行 hook 的注册和移除,这会引发非法操作异常
- 当 JVM 收到 SIGTERM 指令时,hook 操作在一定的时间内没有处理完成,则会被终止
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于