之前在springboot项目启动不报错,但一启动就断开连接问题排查实录一文中,留了一个小尾巴。即如何在springboot项目中不引入web包,也能实现项目启动后,后台能不停止,能一直运行?答案很简单:不让程序停止,那就让主程序一直卡着,官方术语就是,让主程序处于堵塞状态。那如何让主程序处于堵塞状态呢?学过java的同学,main()方法是Java应用程序的入口方法,也就是说,程序在运行的时候,第一个执行的方法就是main()方法。那就在入口处,加个堵塞代码片段就行了。下边就写个简单示例来演示下
1、在启动主程序中,加入堵塞代码片段
堵塞的代码有多种多样,常用的有如下方案
a、写个无限循环方法。不过这种方式不建议使用,太浪费cpu内存
b、利用并发包下提供的一些工具,比如Semaphore。这种实现不占用cpu内存
Semaphore semaphore = new Semaphore(0);
try {
semaphore.acquire();
} catch (InterruptedException e) {
log.error(e.getMessage(),e);
}
c、添加io堵塞片段,比如socket连接代码,最简单的堵塞io片段是如下
System.in.read();
按上述方法实现后,启动项目后,就可以一直处于后台运行。
有启动,就有关闭,在linux环境中,我们关闭一个项目,可能会执行 kill -9 pid把进程关闭。然而这种关闭对业务可能是有损的,比如你后台程序在跑业务线程,此时执行kill -9 pid,就可能会导致业务逻辑处理中断,导致业务出错。因此我们需要一种能平滑关闭的的机制来关闭项目。这边提供两种关闭方案
2、平滑关闭代码
a、在程序中添加addShutdownHook方法
这个方法的意思就是在jvm中增加一个关闭的钩子,当jvm关闭的时候,会执行系统中已经设置的所有通过方法addShutdownHook添加的钩子,当系统执行完这些钩子后,jvm才会关闭。所以这些钩子可以在jvm关闭的时候进行内存清理、对象销毁、关闭连接等操作。addShutdownHook遇到如下场景会被调用
示例代码
public void release(Runnable runnable){
Runtime.getRuntime().addShutdownHook(new Thread(runnable));
}
b、程序中通过Signal类注册信号监听kill信号,在Linux下支持的信号(具体信号kill -l命令查看)
示例代码
public class ShutDownHookSignalHandler implements SignalHandler {
@Override
public void handle(Signal signal) {
SignalType signalType = SignalType.getSignal(signal.getName());
switch (signalType){
//kill -12
case USR2:
callbackByUSR1();
break;
//kill -15
case TERM:
break;
}
}
private void callbackByUSR1(){
boolean isClose = ThreadPoolUtil.INSTANCE.close();
log.info("thread pool isClose:{}",isClose);
}
public void registerSignal(String signalName) {
Signal signal = new Signal(signalName);
Signal.handle(signal, this);
}
}
c、Signal类注册信号监听和addShutdownHook使用的区别
Signal中的handle方法会在进程被kill时收到信号,对main函数的运行不会有任何影响,而使用addShutdownHook,当进程被kill的时候main函数就已经结束了,仅会运行shutdownHook中run()方法的代码。
此外addShutdownHook方法和Signal中handle方法中如果再调用System.exit,会造成死锁,使进程无法正常退出
基于上述原因,我们就可以在代码中同时使用这两种方法
3、示例演示
a、项目启动
b、项目关闭,先执行kill -12 pid
再执行kill -15 pid
创建一直运行的后台程序,主要就是保持主程序堵塞。其次通过signal监听信号量和addShutdownHook配合使用,就可以达到平滑关闭程序的效果。在开发中,可以写一个脚本,先通过kill -12,修改线程池等状态,不再接受新资源,待原有资源处理完毕后。执行kill -15,关闭所有连接,并进行后续一些操作
https://github.com/lyb-geek/springboot-learning/tree/master/springboot-exclude-web