首页
学习
活动
专区
圈层
工具
发布

发现谁用 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 信号时,它会:

拒绝新请求;

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

再执行@PreDestroy注解的方法;

最后退出。

比如这样:

@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是最后的核按钮,不是常规操作。 生产上关停服务,正确顺序应该是:

发SIGTERM(kill -15);

确认服务停止;

清理残留资源;

再重新启动。

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

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

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

  • 发表于:
  • 原文链接https://page.om.qq.com/page/O-m6GoJAM1dvIW9y4zC_KTKQ0
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。
领券