发现谁用 kill -9 关闭程序就开除!

最近我真是气笑了。一个线上服务挂了,日志啥都没有,监控也没捕到异常,最后发现是有人直接一手 kill -9 给干掉的。兄弟啊,这都2025年了,用 kill -9 关服务还不打招呼,这操作放在生产环境,简直是自杀式部署。

为什么不能随便 kill -9

你看啊,kill -9 干的事很简单粗暴:直接向进程发送 SIGKILL 信号,让系统内核强制把进程从内存里清理掉。听起来挺爽的对吧?但问题是——应用压根没机会“收尾”。

一般我们优雅关停服务的时候,比如用 kill -15(也就是 SIGTERM),程序还能捕获到这个信号,做一些收尾动作,比如:

  • 把还没写完的日志落盘;

  • 把数据库连接、MQ通道、线程池这些资源释放掉;

  • 把内存缓存持久化;

  • 通知注册中心“我下线啦”。

但 kill -9 就像是拔电源关电脑,啥清理都没做。日志写一半,缓存还在内存里,数据库连接没断干净,ZooKeeper节点还挂着。你再启动程序,分分钟报错:“端口被占用”、“连接池异常”、“文件锁未释放”……

有时候你以为“重启服务”就能解决问题,但其实你只是把烂摊子藏进了地毯下面。

正确姿势:优雅停机

咱写服务端程序,尤其是 Spring Boot 的那种,优雅停机是必须要做的。要在 JVM 收到退出信号的时候,把清理逻辑跑完再退。

比如下面这段 Java 代码,能优雅地捕获信号,做点收尾工作:

public class GracefulShutdownDemo {

    public static void main(String[] args) {
        System.out.println("服务启动成功... PID: " + ProcessHandle.current().pid());

        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            System.out.println("收到关闭信号,开始清理资源...");
            try {
                Thread.sleep(2000); // 模拟资源清理
                System.out.println("资源清理完毕,程序安全退出。");
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }));

        while (true) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                break;
            }
        }
    }
}

你可以这样测试:

# 启动程序
java GracefulShutdownDemo

# 优雅关闭
kill -15 <pid>

# 或者
ctrl + c

日志里你会看到 “资源清理完毕” 的输出。

但如果你用 kill -9 <pid>,程序根本来不及执行 ShutdownHook,直接消失,连个告别都没有。

那为啥有人非要用 kill -9?

大部分情况都是因为——程序卡死了

要么是死锁、要么是线程池爆满、要么是某个 while(true) 忘了 sleep。开发者一看不动了,就直接 kill -9 一刀切。问题解决得快,但代价也大。

更常见的还有一种情况,就是在 CI/CD 脚本里偷懒,写了:

ps -ef | grep my-app | grep -v grep | awk '{print $2}' | xargs kill -9

然后再 nohup java -jar my-app.jar &好家伙,连是否停干净都不检查,这脚本一跑就是线上“惊喜”。

正确的做法其实很简单,先发 SIGTERM

ps -ef | grep my-app | grep -v grep | awk '{print $2}' | xargs kill -15

然后等5秒看看服务是否自动退出。 如果还卡着,再考虑 kill -9,那是最后一根稻草。

Spring Boot 自带优雅停机功能

其实 Spring Boot 从 2.3 版本开始就支持优雅停机了。只要你在 application.yml 里加上:

server:
  shutdown: graceful
spring:
  lifecycle:
    timeout-per-shutdown-phase: 30s

当服务收到 SIGTERM 信号时,它会:

  1. 拒绝新请求;

  2. 等待正在处理的请求完成;

  3. 再执行 @PreDestroy 注解的方法;

  4. 最后退出。

比如这样:

@Component
public class ResourceCleaner {

    @PreDestroy
    public void onDestroy() {
        System.out.println("Spring Boot 正在优雅停机,释放资源...");
        closeDbConnections();
        shutdownThreadPools();
    }

    private void closeDbConnections() {
        // 模拟关闭数据库连接
        System.out.println("数据库连接已关闭");
    }

    private void shutdownThreadPools() {
        System.out.println("线程池已销毁");
    }
}

你再执行:

kill -15 <pid>

控制台输出:

Spring Boot 正在优雅停机,释放资源...
数据库连接已关闭
线程池已销毁

很干净,是吧。

实战中“kill -9”造成的灾难

我见过几个特别惨的例子:

  • 某支付系统有人手动 kill -9 杀服务,结果 Redis 的分布式锁没释放,后续订单全卡死;

  • 某业务用 MyBatis 批量写数据库,刚写到一半被 kill -9,事务没提交也没回滚,数据半拉子;

  • 某微服务在注册中心没下线,流量还在打过去,结果调用方疯狂报 Connection refused

  • 还有人 kill -9 后立刻拉起服务,端口还没释放干净,直接启动失败。

所以那句话真不是玩笑话:

“发现谁用 kill -9 关闭程序就开除” ——不是因为脾气大,是因为真的贵。

最后

kill -9 是最后的核按钮,不是常规操作。 生产上关停服务,正确顺序应该是:

  1. 发 SIGTERMkill -15);

  2. 确认服务停止;

  3. 清理残留资源;

  4. 再重新启动。

这点看似小事,实际上体现了一个工程师的职业素养。你可以写再多的业务逻辑,但只要线上一个 kill -9 下去,日志没了、数据坏了、锁卡了,别人只会说一句——“这是谁写的程序?”

所以啊,除非你是在救火,否则别碰 kill -9。

真要杀,也得知道自己在干啥。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值