zoukankan      html  css  js  c++  java
  • 【微服务】优雅停机

    参考:

    https://www.linuxprobe.com/micro-service-fa.html

    https://www.cnblogs.com/shihaiming/p/11408684.html

    https://blog.csdn.net/j3T9Z7H/article/details/82836811

    https://blog.csdn.net/w1014074794/article/details/89447401/

    https://bhuwanupadhyay.github.io/posts/graceful-shutdown-in-spring-boot/

    Springboot中的优雅关机

    应用场景:例如项目升级或服务搬迁,需要大规模关闭当前线上服务时。

    反面后果:若采用强硬关机(kill -9),则服务会立即停止,客户从此不会收到任何响应,对服务端关闭未知。

    亟待改进:在服务终止命令发出后, 程序应该能拒绝新的请求, 但应该继续完成已有请求的处理。

    原理: 

    https://blog.csdn.net/weixin_37703598/article/details/89702616?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-0&spm=1001.2101.3001.4242


    ============================
    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 的进程)

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

     Demo1:

    https://blog.csdn.net/liwei128/article/details/95794721

    import java.util.concurrent.Executor;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    import org.apache.catalina.connector.Connector;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
    import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
    import org.springframework.context.ApplicationListener;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.event.ContextClosedEvent;
    import org.springframework.stereotype.Component;
    /**
    * Tomcat优雅停机
    * @author LW
    * @time 2019年7月13日 上午12:05:47
    */
    @Component
    public class GracefulShutdownTomcat implements ApplicationListener<ContextClosedEvent> {
    private final Logger log = LoggerFactory.getLogger(GracefulShutdownTomcat.class);
    private final int waitTime = 30;
    private static volatile Connector connector;

    /**
    * 用于获取tomcat连接器
    * @return
    */
    @Bean
    public ServletWebServerFactory servletContainer() {
    TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
    tomcat.addConnectorCustomizers(connect->{
    connector = connect;
    });
    return tomcat;
    }

    /**
    * 监听 停止操作kill -15
    */
    @Override
    public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {
    long startTime = System.currentTimeMillis();
    //tomcat暂停对外服务
    connector.pause();
    //获取tomcat线程池
    Executor executor = connector.getProtocolHandler().getExecutor();
    if (executor instanceof ThreadPoolExecutor) {
    try {
    ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
    //线程池优雅停止(不接收新的请求,等待任务运行完成后关闭线程池)
    threadPoolExecutor.shutdown();
    //堵塞等待一定时间,指定时间内关闭成功则返回true,解除堵塞;否则fasle
    if (threadPoolExecutor.awaitTermination(waitTime, TimeUnit.SECONDS)) {
    log.info("Tomcat thread pool closed,time:{}ms",System.currentTimeMillis()-startTime);
    }
    } catch (InterruptedException ex) {
    Thread.currentThread().interrupt();
    }
    }
    }
    }

     Demo2:

    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(kill -2 pid), 程序很快就结束了, 最终的输出是:

    ============================
    SpringBoot Web 项目的优雅停机
    ============================
    Java web 服务器通常也支持优雅退出, 比如 tomcat, 提供如下命令:
    catalina.sh stop n         , 先等 n 秒后, 然后停止 tomcat. 
    catalina.sh stop n -force  , 先等 n 秒后, 然后 kill -9 tomcat. 

    SpringBoot Web 项目, 如果使用的是外置 tomcat, 可以直接使用上面 tomcat 命令完成优雅停机. 但通常使用的是内置 tomcat 服务器, 这时就需要编写代码来支持优雅停止. 

    应用场景之一:

    https://stackoverflow.com/questions/39157863/how-to-shutdown-activemq-session-thread-gracefully-on-tomcat

  • 相关阅读:
    深度分页,不存在的
    netty做客户端实战(二)
    netty服务端实战(一)
    netty做上位机主动同时连接多个下位机时,接受报文报 java.lang.IndexOutOfBoundsException: srcIndex: 0异常错误
    使用线程实现多生产者,多消费者demo
    vscode中vue安装,使用的插件
    实用的RBAC模型
    Springboot+thymeleaf+shiro整合
    Log4j作用
    SpringBoot整合thymeleaf
  • 原文地址:https://www.cnblogs.com/cathygx/p/14486452.html
Copyright © 2011-2022 走看看