JVM Shutdown Hook详解


版权
什么是JVM Shutdown Hook
Shutdown Hook是一种比较特殊的结构,一般用于在JVM关闭之前,需要执行的一些操作的时候。常见的,比如说你的程序退出时需要做一些清理工作的时候,就可以考虑使用Shutdown Hook。但是如果你的JVM是非正常退出的,比如接收了SIGKILL,这个时候就不能保证shutdown hook能够正常执行。

如何使用Shutdown Hook
Java中其实已经提供了相应的接口来实现,使用起来也非常方便。以下给出几个简单的例子:

public class ShutDownHookTest
{
  public static void main(String[] args)
  {
  
    Runtime.getRuntime().addShutdownHook(new Thread()
    {
      public void run()
      {
        System.out.println("Shutdown Hook now!");
      }
    });
    System.out.println("Going to exit");
  }
}

输出:

Going to exit
Shutdown Hook now!

以上例子可以看到,我们只需要给addShutdownHook里传入一个Thread对象,在run()中写入需要执行的操作。


class MyThread extends Thread {     
    public void run() {         
        System.out.println("Execute done");
        System.out.println("Shutdown hook now!");
    }
}
  
class ShutDownHookTest {
      
    public static void main(String[] args) {
        Runtime.getRuntime().addShutdownHook(new MyThread());
  
        for(int i = 1; i < 4; i++) 
            System.out.println("Execute:" + i);
    }
}

输出:

Execute:1
Execute:2
Execute:3
Execute done
Shutdown hook now!

以上例子可以清晰看到,当主程序执行完后,才会开始执行Shutdown Hook, JVM才会退出。

Shutdown Hook的特性
Shutdown Hook不能保证一定执行
如果JVM crashe了, Shutdown Hook并不能保证一定执行。例如如果收到了SIGKILL的时候,程序会立刻终止执行,JVM立刻退出,导致没有机会执行Shutdown Hook.如果调用了Runtime.halt()的情况下,也可以导致JVM在没有执行Shutdown Hook的时候直接退出。当然,如果一个程序正常结束,会在JVM退出去调用Shutdown Hook。如果JVM因为用户要求中断或者是接受到SIGTERM,也是会调用Shutdown Hook的。

Shutdown Hook是可以被强制中断的
即使已经开始执行Shutdown Hook,也是可以被中断的,比如当接收到了SIGTERM,但是shutdown hook没有在一定时间内完成,也是会被强制中断,导致shutdown hook没有完整执行。所以一般在Shutdown hook中的操作都应该是可以快速执行完毕,不应该是一些long time的任务。

Shutdown Hook可以有多个
一个程序中可以注册多个shutdown hook,但是JVM执行shutdown hook的时候是一个任意顺序,并且JVM执行hook的时候是并发的。

Shutdown hook中不可以regist/unregister shutdown hook
如果这么做了,JVM会抛IllegalStateException。

Shutdown hook可以被停止
如果一个Shutdown已经开始执行了,除了例如SIGKILL这样的外部干预,需要且只能通过Runtime.halt()中断。

Shutdown hook需要安全权限
Shutdown hook在Spark中的应用实例
在这里我们用Spark中的ApplicationMaster来做一个实例分析。
ApplicationMaster.scala

// This shutdown hook should run *after* the SparkContext is shut down.
      val priority = ShutdownHookManager.SPARK_CONTEXT_SHUTDOWN_PRIORITY - 1
      ShutdownHookManager.addShutdownHook(priority) { () =>
        val maxAppAttempts = client.getMaxRegAttempts(sparkConf, yarnConf)
        val isLastAttempt = appAttemptId.getAttemptId() >= maxAppAttempts

        if (!finished) {
          // The default state of ApplicationMaster is failed if it is invoked by shut down hook.
          // This behavior is different compared to 1.x version.
          // If user application is exited ahead of time by calling System.exit(N), here mark
          // this application as failed with EXIT_EARLY. For a good shutdown, user shouldn't call
          // System.exit(0) to terminate the application.
          finish(finalStatus,
            ApplicationMaster.EXIT_EARLY,
            "Shutdown hook called before final status was reported.")
        }

        if (!unregistered) {
          // we only want to unregister if we don't want the RM to retry
          if (finalStatus == FinalApplicationStatus.SUCCEEDED || isLastAttempt) {
            unregister(finalStatus, finalMsg)
            cleanupStagingDir(new Path(System.getenv("SPARK_YARN_STAGING_DIR")))
          }
        }

这段程序中,当application结束时需要删除stagingDir下的文件。这就是我们上文提到的shutdown hook常见用法。

但在实际Spark application中,有时候会碰到application被kill或者异常退出的情况,这个时候去观察stagingDir会发现,application结束后它并没有被删除。因为当application被kill或者异常退出时,Yarn会发送SIGTERM,随后发送SIGKILL。shutdown hook需要在这两个操作中间完成,如果没有及时完成,就会导致shutdown hook里的删除stagingDir的操作没有完成。这个时间可以通过yarn.nodemanager.slepp-delay-before-sigkill.ms来设置。

参考
http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Runtime.html#addShutdownHook(java.lang.Thread)
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值