JVM应用优雅上下线,再也不担心抖动了

开发 前端
通常JVM可使用runtime.addShutdownHook()对退出信号做处理,它让我们在程序正常退出或者发生异常时能有机会做一些清场工作。

一、前言

JVM的关闭方式可以分为三种:

1.正常关闭:当最后一个非守护线程结束、或者调用了System.exit、或者通过其他特定平台的方法关闭(发送SIGINT,SIGTERM信号等)

2.强制关闭:通过调用Runtime.halt方法、或者是在操作系统中直接kill(发送SIGKILL信号)掉JVM进程

3.异常关闭:运行中遇到RuntimeException异常、OOM错误等。

二、ShutdownHook

通常JVM可使用runtime.addShutdownHook()对退出信号做处理,它让我们在程序正常退出或者发生异常时能有机会做一些清场工作。关闭钩子其实可以看成是一个已经初始化了的但还没启动的线程,当JVM关闭时会并发无序地执行注册的所有关闭钩子。

Runtime.getRuntime().addShutdownHook(handleThread);    //handleThread是信号处理线程。

ShutdownHook响应的信号如下:

  • 1(SIGHUP):如果使用了nohup则不响应;
  • 2(SIGINT):如果使用了后台&则不响应;
  • 15(SIGTERM):都响应。

注意事项:

  • 不要使用kill -9来结束进程,这样ShutdownHook得不到执行;
  • ShutdownHook要尽量短。计算机在关机前,会给所有的进程发送一个SIGTERM信号,等若干秒后就直接发送SIGKILL了;
  • ShutdownHook要保证线程安全。如果多次发送信号,那么ShutdownHook被不同的线程多次执行。

三、SignalHandler

用户可以自定义SignalHander对特定信号进行处理。

class MySignalHandler implements SignalHandler
{
   public static void listenTo(String name) {
      Signal signal = new Signal(name);
      Signal.handle(signal, new MySignalHandler());
   }
  
   public void handle(Signal signal) {
      System.out.println("Signal: " + signal);
      if (signal.toString().trim().equals("SIGTERM")) {
         System.out.println("SIGTERM raised, terminating...");
         System.exit(1);
      }
   }
}

Java对每个信号都启动一个线程进行处理。注册TERM信号,就启动"SIGTERM handler" 线程。即便主线程被阻塞,信号依然可以得到处理。由于对信号的处理是多线程的,所以应保证信号处理实例SignalHandler应该是线程安全的。

四、总结

  • ShutdownHook只响应1(SIGHUP)、2(SIGINT)、15(SIGTERM)三种信号,而JVM一般用nohup...&的方式启动,所以会忽略1、2两种信号;
  • ShutdownHook触发时,多个钩子会并发无序执行。如果资源关闭上有先后依赖则会有问题;

4.1 优雅关闭

由于ShutdownHook的并发无序执行,所以我们在优雅关闭时不能直接kill -15,比如有残留请求的情况,如果部分资源已关闭,那么残留请求的执行会有异常。 正确流程如下:

  1. kill -12:等待10s。用户自定义SignalHandler处理12信号,而且此时所有的资源都是正常状态。1)告知上游该服务已关闭,不要再发请求;2)处理残留的请求;3)其他需要正常关闭的操作。
  2. kill -15:等待10s。这时会并发无序执行注册的ShutdownHook,进行一些资源的释放,很有可能不需要10sJVM就退出了。
  3. kill -9:如果kill -15还没有终止JVM,则直接强制退出。

这里的优雅就体现在第一步的10秒kill -12,在资源都正常的情况下给业务一些时间来正常关闭服务。 

 4.2 示例

我们以转转的RPC框架ZZSCF为例,来看其是如何实现优雅关闭的。

4.2.1 kill -12

首先,我们进行kil -12并等待10秒,用户自定义SignalHandler来处理12信号,而且此时所有的资源都是正常状态。

图片图片

图片图片

图片图片

图片图片

图片图片

图片图片

4.2.2 kill -15

接着,我们进行kil -15并等待10秒。这时会并发无序执行注册的ShutdownHook,进行一些资源的释放,很有可能不需要10sJVM就退出了。

4.2.3 kill -9

最后,如果kill -15还没有终止JVM,则直接强制退出。

五、启动脚本DEMO

这里附赠常用JVM的重启脚本。                                                                            

用法:./main.sh start|stop|restart|kill|status

查看源码链接:https://github.com/waterystone/shell_test/blob/main/jvm/main.sh

六、参考

  • How to gracefully handle the SIGKILL signal in Java:https://stackoverflow.com/questions/2541597/how-to-gracefully-handle-the-sigkill-signal-in-java
  • 服务如何优雅关闭:https://juejin.cn/post/6844903814181421064
责任编辑:武晓燕 来源: 转转技术
相关推荐

2021-12-21 09:05:46

命令Linux敲错

2020-04-30 09:19:56

Docker容器虚拟机

2021-06-08 07:48:26

数据 Python开发

2015-05-29 09:01:48

2018-10-30 16:10:41

Linux救援模式命令

2020-01-21 21:15:16

WiFi网络WiFi6

2021-12-06 15:02:37

RabbitMQ系统消息

2020-06-15 08:03:17

大文件OOM内存

2022-01-27 08:27:23

Dubbo上下线设计

2021-04-20 10:20:27

Dubbo网络通信通信协议

2023-06-06 08:11:09

kafka事务消息语义

2021-08-13 22:38:36

大数据互联网技术

2021-09-30 22:46:05

微信安全支付

2015-10-22 10:38:43

Wi-Fi燃气报警器

2020-04-10 09:55:28

Git 工具黑魔法

2021-06-04 12:05:03

Redis Bitmap 数据库

2019-09-04 10:00:07

手机人脸识别

2022-09-14 08:02:25

加密算法Bcryp

2021-06-11 07:14:04

QQ音乐微信翻译
点赞
收藏

51CTO技术栈公众号