再谈spring boot 优雅停机
何为优雅关机
线程池,shutdown(不接受新任务等待处理完)还是shutdownNow(调用 Thread.interrupt进行中断)
socket 链接,比如:netty、mq
告知注册中心快速下线(靠心跳机制客服早都跳起来了),比如:eureka
清理临时文件,比如:poi
各种堆内堆外内存释放
kill指令
#查看jvm进程pidjps#列出所有信号名称kill -l# Windows下信号常量值# 简称 全称 数值# INT SIGINT 2 Ctrl+C中断# ILL SIGILL 4 非法指令# FPE SIGFPE 8 floating point exception(浮点异常)# SEGV SIGSEGV 11 segment violation(段错误)# TERM SIGTERM 5 Software termination signal from kill(Kill发出的软件终止)# BREAK SIGBREAK 21 Ctrl-Break sequence(Ctrl+Break中断)# ABRT SIGABRT 22 abnormal termination triggered by abort call(Abort)#linux信号常量值# 简称 全称 数值# HUP SIGHUP 1 终端断线# INT SIGINT 2 中断(同 Ctrl + C)# QUIT SIGQUIT 3 退出(同 Ctrl + \)# KILL SIGKILL 9 强制终止# TERM SIGTERM 15 终止# CONT SIGCONT 18 继续(与STOP相反, fg/bg命令)# STOP SIGSTOP 19 暂停(同 Ctrl + Z)#....#可以理解为操作系统从内核级别强行杀死某个进程kill -9 pid#理解为发送一个通知,等待应用主动关闭kill -15 pid#也支持信号常量值全称或简写(就是去掉SIG后)kill -l KILL
public interface SignalHandler {SignalHandler SIG_DFL = new NativeSignalHandler(0L);SignalHandler SIG_IGN = new NativeSignalHandler(1L);void handle(Signal var1);}class Terminator {private static SignalHandler handler = null;Terminator() {}//jvm设置SignalHandler,在System.initializeSystemClass中触发static void setup() {if (handler == null) {SignalHandler var0 = new SignalHandler() {public void handle(Signal var1) {Shutdown.exit(var1.getNumber() + 128);//调用Shutdown.exit}};handler = var0;try {Signal.handle(new Signal("INT"), var0);//中断时} catch (IllegalArgumentException var3) {;}try {Signal.handle(new Signal("TERM"), var0);//终止时} catch (IllegalArgumentException var2) {;}}}}
Runtime.addShutdownHook
public class Runtime {public void addShutdownHook(Thread hook) {SecurityManager sm = System.getSecurityManager();if (sm != null) {sm.checkPermission(new RuntimePermission("shutdownHooks"));}ApplicationShutdownHooks.add(hook);}}class ApplicationShutdownHooks {/* The set of registered hooks */private static IdentityHashMap<Thread, Thread> hooks;static synchronized void add(Thread hook) {if(hooks == null)throw new IllegalStateException("Shutdown in progress");if (hook.isAlive())throw new IllegalArgumentException("Hook already running");if (hooks.containsKey(hook))throw new IllegalArgumentException("Hook previously registered");hooks.put(hook, hook);}}//它含数据结构和逻辑管理虚拟机关闭序列class Shutdown {/* Shutdown 系列状态*/private static final int RUNNING = 0;private static final int HOOKS = 1;private static final int FINALIZERS = 2;private static int state = RUNNING;/* 是否应该运行所以finalizers来exit? */private static boolean runFinalizersOnExit = false;// 系统关闭钩子注册一个预定义的插槽.// 关闭钩子的列表如下:// (0) Console restore hook// (1) Application hooks// (2) DeleteOnExit hookprivate static final int MAX_SYSTEM_HOOKS = 10;private static final Runnable[] hooks = new Runnable[MAX_SYSTEM_HOOKS];// 当前运行关闭钩子的钩子的索引private static int currentRunningHook = 0;/* 前面的静态字段由这个锁保护 */private static class Lock { };private static Object lock = new Lock();/* 为native halt方法提供锁对象 */private static Object haltLock = new Lock();static void add(int slot, boolean registerShutdownInProgress, Runnable hook) {synchronized (lock) {if (hooks[slot] != null)throw new InternalError("Shutdown hook at slot " + slot + " already registered");if (!registerShutdownInProgress) {//执行shutdown过程中不添加hookif (state > RUNNING)//如果已经在执行shutdown操作不能添加hookthrow new IllegalStateException("Shutdown in progress");} else {//如果hooks已经执行完毕不能再添加hook。如果正在执行hooks时,添加的槽点小于当前执行的槽点位置也不能添加if (state > HOOKS || (state == HOOKS && slot <= currentRunningHook))throw new IllegalStateException("Shutdown in progress");}hooks[slot] = hook;}}/* 执行所有注册的hooks*/private static void runHooks() {for (int i=0; i < MAX_SYSTEM_HOOKS; i++) {try {Runnable hook;synchronized (lock) {// acquire the lock to make sure the hook registered during// shutdown is visible here.currentRunningHook = i;hook = hooks[i];}if (hook != null) hook.run();} catch(Throwable t) {if (t instanceof ThreadDeath) {ThreadDeath td = (ThreadDeath)t;throw td;}}}}/* 关闭JVM的操作*/static void halt(int status) {synchronized (haltLock) {halt0(status);}}//JNI方法static native void halt0(int status);// shutdown的执行顺序:runHooks > runFinalizersOnExitprivate static void sequence() {synchronized (lock) {/* Guard against the possibility of a daemon thread invoking exit* after DestroyJavaVM initiates the shutdown sequence*/if (state != HOOKS) return;}runHooks();boolean rfoe;synchronized (lock) {state = FINALIZERS;rfoe = runFinalizersOnExit;}if (rfoe) runAllFinalizers();}//Runtime.exit时执行,runHooks > runFinalizersOnExit > haltstatic void exit(int status) {boolean runMoreFinalizers = false;synchronized (lock) {if (status != 0) runFinalizersOnExit = false;switch (state) {case RUNNING: /* Initiate shutdown */state = HOOKS;break;case HOOKS: /* Stall and halt */break;case FINALIZERS:if (status != 0) {/* Halt immediately on nonzero status */halt(status);} else {/* Compatibility with old behavior:* Run more finalizers and then halt*/runMoreFinalizers = runFinalizersOnExit;}break;}}if (runMoreFinalizers) {runAllFinalizers();halt(status);}synchronized (Shutdown.class) {/* Synchronize on the class object, causing any other thread* that attempts to initiate shutdown to stall indefinitely*/sequence();halt(status);}}//shutdown操作,与exit不同的是不做halt操作(关闭JVM)static void shutdown() {synchronized (lock) {switch (state) {case RUNNING: /* Initiate shutdown */state = HOOKS;break;case HOOKS: /* Stall and then return */case FINALIZERS:break;}}synchronized (Shutdown.class) {sequence();}}}
spring 3.2.12
public abstract class AbstractApplicationContext extends DefaultResourceLoader {public void registerShutdownHook() {if (this.shutdownHook == null) {// No shutdown hook registered yet.this.shutdownHook = new Thread() {@Overridepublic void run() {doClose();}};Runtime.getRuntime().addShutdownHook(this.shutdownHook);}}protected void doClose() {boolean actuallyClose;synchronized (this.activeMonitor) {actuallyClose = this.active && !this.closed;this.closed = true;}if (actuallyClose) {if (logger.isInfoEnabled()) {logger.info("Closing " + this);}LiveBeansView.unregisterApplicationContext(this);try {//发布应用内的关闭事件publishEvent(new ContextClosedEvent(this));}catch (Throwable ex) {logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);}// 停止所有的Lifecycle beans.try {getLifecycleProcessor().onClose();}catch (Throwable ex) {logger.warn("Exception thrown from LifecycleProcessor on context close", ex);}// 销毁spring 的 BeanFactory可能会缓存单例的 Bean.destroyBeans();// 关闭当前应用上下文(BeanFactory)closeBeanFactory();// 执行子类的关闭逻辑onClose();synchronized (this.activeMonitor) {this.active = false;}}}}public interface LifecycleProcessor extends Lifecycle {/*** Notification of context refresh, e.g. for auto-starting components.*/void onRefresh();/*** Notification of context close phase, e.g. for auto-stopping components.*/void onClose();}
spring boot
#启用shutdownendpoints.shutdown.enabled=true#禁用密码验证endpoints.shutdown.sensitive=false#可统一指定所有endpoints的路径management.context-path=/manage#指定管理端口和IPmanagement.port=8088management.address=127.0.0.1#开启shutdown的安全验证(spring-security)endpoints.shutdown.sensitive=true#验证用户名security.user.name=admin#验证密码security.user.password=secret#角色management.security.role=SUPERUSER
@ConfigurationProperties(prefix = "endpoints.shutdown")public class ShutdownMvcEndpoint extends EndpointMvcAdapter {public ShutdownMvcEndpoint(ShutdownEndpoint delegate) {super(delegate);}//post请求@PostMapping(produces = {"application/vnd.spring-boot.actuator.v1+json", "application/json"})@ResponseBodypublic Object invoke() {return !this.getDelegate().isEnabled() ? new ResponseEntity(Collections.singletonMap("message", "This endpoint is disabled"), HttpStatus.NOT_FOUND) : super.invoke();}}@ConfigurationProperties(prefix = "endpoints.shutdown")public class ShutdownEndpoint extends AbstractEndpoint<Map<String, Object>> implements ApplicationContextAware {private static final Map<String, Object> NO_CONTEXT_MESSAGE = Collections.unmodifiableMap(Collections.singletonMap("message", "No context to shutdown."));private static final Map<String, Object> SHUTDOWN_MESSAGE = Collections.unmodifiableMap(Collections.singletonMap("message", "Shutting down, bye..."));private ConfigurableApplicationContext context;public ShutdownEndpoint() {super("shutdown", true, false);}//执行关闭public Map<String, Object> invoke() {if (this.context == null) {return NO_CONTEXT_MESSAGE;} else {boolean var6 = false;Map var1;class NamelessClass_1 implements Runnable {NamelessClass_1() {}public void run() {try {Thread.sleep(500L);} catch (InterruptedException var2) {Thread.currentThread().interrupt();}//这个调用的就是AbstractApplicationContext.closeShutdownEndpoint.this.context.close();}}try {var6 = true;var1 = SHUTDOWN_MESSAGE;var6 = false;} finally {if (var6) {Thread thread = new Thread(new NamelessClass_1());thread.setContextClassLoader(this.getClass().getClassLoader());thread.start();}}Thread thread = new Thread(new NamelessClass_1());thread.setContextClassLoader(this.getClass().getClassLoader());thread.start();return var1;}}}
引用资料
kill help:https://linux.die.net/man/1/kill
————————————————
版权声明:本文为CSDN博主「布道」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/alex_xfboy/article/details/90404691
