码迷,mamicode.com
首页 > 编程语言 > 详细

SpringBoot系列: 如何优雅停止服务

时间:2018-12-26 15:57:16      阅读:219      评论:0      收藏:0      [点我收藏+]

标签:没有   new   关闭   而不是   指令   停止   中断   不能   状态   

============================
背景
============================
在系统生命周期中, 免不了要做升级部署, 对于关键服务, 我们应该能做到不停服务完成升级 (perform a zero downtime upgrade), 对于一般系统, 应该做到优雅地停服务.

如何做到不停服务的升级? 需要做到下面两点:
1. 服务本身应该部署多份, 前面应该有 LVS/Haproxy 层或者服务注册组件.
2. 每一份服务能被优雅停机, 即: 在 kill pid 命令发出后, 程序应该能拒绝新的请求, 但应该继续完成已有请求的处理.

本文重点关注如何支持优雅停机.


============================
Linux kill 命令
============================
kill 命令常用的信号选项:
(1) kill -2 pid 向指定 pid 发送 SIGINT 中断信号, 等同于 ctrl+c.
(2) kill -9 pid, 向指定 pid 发送 SIGKILL 立即终止信号.
(3) kill -15 pid, 向指定 pid 发送 SIGTERM 终止信号.
(4) kill pid 等同于 kill 15 pid

SIGINT/SIGKILL/SIGTERM 信号的区别:
(1) SIGINT (ctrl+c) 信号 (信号编号为 2), 信号会被当前进程树接收到, 也就说, 不仅当前进程会收到该信号, 而且它的子进程也会收到.
(2) SIGKILL 信号 (信号编号为 9), 程序不能捕获该信号, 最粗暴最快速结束程序的方法.
(3) SIGTERM 信号 (信号编号为 15), 信号会被当前进程接收到, 但它的子进程不会收到, 如果当前进程被 kill 掉, 它的的子进程的父进程将变成 init 进程 (init 进程是那个 pid 为 1 的进程)

一般要结束某个进程, 我们应该优先使用 kill pid , 而不是 kill -9 pid. 如果对应程序提供优雅关闭机制的话, 在完全退出之前, 先可以做一些善后处理.


============================
Java 对于优雅停机的底层支持
============================
Java 语言底层有机制能捕获到 OS 的 SIGINT/ SIGTERM 停止指令的, 具体是通过 Runtime.getRuntime().addShutdownHook() 向 JVM 中注册一个 Shutdown hook 线程, 当 JVM 收到停止信号后, 该线程将被激活运行, 这时候我们就可以向其他线程发出中断指令, 进而快速而优雅地关闭整个程序.

public class Test {
    public static void main(String[] args){
        System.out.println("1: Main start");

        Thread mainThread = Thread.currentThread();

        //注册一个 ShutdownHook
        ShutdownSampleHook thread=new ShutdownSampleHook(mainThread);
        Runtime.getRuntime().addShutdownHook(thread);

        try {
            Thread.sleep(30*1000);
        } catch (InterruptedException e) {
            System.out.println("3: mainThread get interrupt signal.");
        }

        System.out.println("4: Main end");  
    }
}

class ShutdownSampleHook extends Thread {
    private Thread mainThread;
    @Override
    public void run() {
        System.out.println("2: Shut down signal received.");
        mainThread.interrupt();//给主线程发送一个中断信号
        try {
            mainThread.join(); //等待 mainThread 正常运行完毕
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("5: Shut down complete.");
    }

    public ShutdownSampleHook(Thread mainThread) {
        this.mainThread=mainThread;

    }
}

 

关于 mainThread.interrupt() 方法的说明, 该方法将给主线程发送一个中断信号. 如果主线程没有进入阻塞状态, interrupt() 其实没有什么作用; 如果主线程处于阻塞状态, 该线程将得到一个 InterruptedException 异常. 在调用 mainThread.join() 或 mainThread.wait() 之前, 仍可以通过调用 mainThread.interrupted() 来清除中断信号.
一个线程有三种进入阻塞状态的方法, 分别是调用 Thread.wait() 或 Thread.join() 或 Thread.sleep().

 

正常情况下, 程序需要运行 30 秒, 程序的输出是:

技术分享图片

 

如果在程序启动后, 按下 Ctrl+C, 程序很快就结束了, 最终的输出是:

技术分享图片

 

SpringBoot系列: 如何优雅停止服务

标签:没有   new   关闭   而不是   指令   停止   中断   不能   状态   

原文地址:https://www.cnblogs.com/harrychinese/p/SpringBoot-graceful-shutdown.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!