SpringBoot项目优雅停机+Pid暴力停机

发布时间 2023-07-15 00:41:37作者: 白嫖老郭

bootstrap.yaml配置项目的pid输出位置

spring:
  pid:
    file: F:/cloud-nacos/cloud_gateway/application.pid

springboot项目修改启动类启动方式

原始启动类
SpringApplication.run(MainApplication.class, args);

@SpringBootApplication
@EnableDiscoveryClient
public class GateWayApp {
    /**
     * @author: GuoTong
     * @date: 2022-10-05 10:42:59
     */
    public static void main(String[] args) {
        try {
            System.setProperty("spring.devtools.restart.enabled", "false");
            SpringApplication springApplication = new SpringApplication(MainApplication.class);
            springApplication.addListeners(new ApplicationPidFileWriter());
            springApplication.run(args);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

优雅停机和暴力停机的接口

package com.gton.shutdown;

import com.alibaba.cloud.commons.io.FileUtils;
import com.alibaba.cloud.commons.lang.StringUtils;
import com.gton.config.Resp;
import io.netty.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.boot.ExitCodeGenerator;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.PreDestroy;
import java.io.File;
import java.io.IOException;

/**
 * @description: 优雅停机?
 * 简单的说,就是向应用进程发出停止指令之后,能保证正在执行的业务操作不受影响,
 * 直到操作运行完毕之后再停止服务。
 * @author: GuoTong
 * @createTime: 2023-07-14 22:32
 * @since JDK 1.8 OR 11
 **/
@RestController
@Slf4j
public class ApplicationContextShutDown implements ApplicationContextAware {

    private ApplicationContext context;


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.context = applicationContext;
    }

    /**
     * Description: 优雅停机
     *
     * @author: GuoTong
     * @date: 2023-07-14 22:46:01
     * @return:void
     */
    @GetMapping(value = {"/ShutDown", "/shutdown"})
    public void shutdownFunction() {
        log.info("Application Context  Doing ShutDown  .......");
        ((ConfigurableApplicationContext) context).close();
    }

    /**
     * Description: 获取进程号
     *
     * @author: GuoTong
     * @date: 2023-07-14 22:46:01
     * @return:void
     */
    @GetMapping(value = {"/queryPid", "/getPid"})
    public Resp queryApplicationPid() {
        String fileInputStreamPid = getPidFunction();
        return Resp.Ok(fileInputStreamPid);
    }


    /**
     * Description: 强行终止进程
     *
     * @author: GuoTong
     * @date: 2023-07-14 22:46:01
     * @return:void
     */
    @GetMapping(value = {"/kill", "/stop", "/termination"})
    public void termination() {
        // 获取上下文环境信息{application.yml}
        String pid = getPidFunction();
        String endingApplicationShell = "kill -9 " + pid;
        // java主要通过Runtime和Process执行Linux命令, Process是Runtime.exec返回值,可以用来对执行过程进行后续操作(获取结果,发送命令,等待结果)。
        try {
            log.info("强行终止进程 ,{}", endingApplicationShell);
            Runtime.getRuntime().exec(endingApplicationShell);
        } catch (IOException e) {
            log.error("强行终止进程失败", e);
        }
    }

    /**
     * Description:  容器销毁前调用
     *
     * @author: GuoTong
     * @date: 2023-07-14 22:42:19
     * @return:
     */
    @PreDestroy
    public void PreDestroy() {
        log.info("springBoot项目已经优雅关闭  .......");
    }

    /**
     * Description:Springboot自身提供的优雅停机
     *
     * @param applicationContext
     * @author: GuoTong
     * @date: 2023-07-14 22:42:08
     * @return:
     */
    public void exitApplication(ApplicationContext applicationContext) {
        // SpringApplication.exit()方法也可以安全的退出程序
        int exit = SpringApplication.exit(applicationContext, (ExitCodeGenerator) () -> 0);
        //同时会返回一个退出码,这个退出码可以传递给所有的context最后通过调用System.exit()可以将这个错误码也传给JVM。
        System.exit(exit);
    }

    /**
     * Description:  获取上下文环境信息{application.yml}PID
     *
     * @author: GuoTong
     * @date: 2023-07-15 00:15:14
     * @return:java.lang.String
     */
    private String getPidFunction() {
        Environment environment = context.getEnvironment();
        String pidFileName = environment.getProperty("spring.pid.file");
        if (StringUtils.isEmpty(pidFileName)) {
            pidFileName = "application.pid";
        }
        String fileInputStreamPid = null;
        try {
            fileInputStreamPid = FileUtils.readFileToString(new File(pidFileName), CharsetUtil.UTF_8);
        } catch (IOException e) {
            log.info("获取Pid失败", e);
        }
        return fileInputStreamPid;
    }

}