zoukankan      html  css  js  c++  java
  • Spring Boot 优雅退出机制

    问题

    最近项目重构,改用 Spring Boot 框架,遇到个问题:当程序 catch 住某些 exception ,需要停掉整个 application ,然后人工介入查看。但是,发现没有办法停掉应用,应用本身也不继续跑下去,它就 hang 在那了。报错如下:

    o.s.c.s.DefaultLifecycleProcessor.stop(387) - Failed to shut down 1 bean with phase value 2147483647 within timeout of 30000ms: [messageListenerContainer]
    

    调查

    定位到相关代码块如下:

    public void onMessage(Message msg) {
    	try {
    		processMsg(msg);
    	} catch (Throwable t) {
    		System.exit(-1);
    	}
    }
    

    收到了JMS消息,但是processMsg报错,然后会被 catch 住,然后执行System.exit,但是失败了。

    这个DefaultLifecycleProcessor是 springframework 的类,在程序 shutdown 的时候会被调用来销毁/关闭 bean 。

    分析

    结合 jstack.review 查看 thread dump 如下:

    PNG

    没有死锁,进一步分析可知,SpringContextShutdownHook想要 shutdown messageListenerContainer,但是后者还在等消息。所以前者 timeout 了。

    解决1

    最简单的方案,退出时不要调用 Spring 的 ShutdownHook ,就不会有后面一系列的问题。在 properties 文件加入一行配置:

    spring.main.register-shutdown-hook=false
    

    这个方案足够简单,也奏效。但是没有合理地关闭资源,可能会造成资源浪费。如果频繁启停应用,可能会有问题。

    解决2

    这个问题的本质是,处理消息的线程不能自己关闭自己的 JMS container 。

    那么,就建一个 monitor 线程,如果需要退出时,发一个信号给 monitor 线程,让它去关闭 JMS container (以及 DataSource, File, etc.) 。示例代码如下:

    Executors.newSingleThreadExecutor.execute(new Runnable() {
    	public void run() {
    		if (signal)
    			stopTheContainer();
    	}
    }
    

    这个signal,可以用一个AtomicBoolean shutdownFlag 来实现。

    解决3

    网上有说升级 Spring Boot version to 2.3.4.RELEASE 就能解决问题的,这个笔者没有试过。仅仅列在这里作为一个可能的选项。

    参考这里 -> https://github.com/spring-cloud/spring-cloud-gateway/issues/2037

    参考

  • 相关阅读:
    c#大圣之路笔记——c# 页面加载数据过长等待显示框
    Python(五)
    Python(四)
    Python(三)
    python(二)数据类型与变量
    初识Python
    Linux初识(九)
    Linux初识(八)正则表达式
    Linux基础初识(七)
    Linux基础初识(六)
  • 原文地址:https://www.cnblogs.com/maxstack/p/15668893.html
Copyright © 2011-2022 走看看