spring web启动失败后进程无法结束,一直报数据库连接失败的问题

环境描述:

操作系统:centos、windows

java服务,使用springboot,web项目

现象:日志中不停的输出错误日志

复制代码
2023-12-23 17:37:32.402|ERROR|main|org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter.report:40|

***************************
APPLICATION FAILED TO START
***************************

Description:

Web server failed to start. Port 1666 was already in use.

Action:

Identify and stop the process that's listening on port 1666 or configure this application to listen on another port.

2023-12-23 17:37:32.461|ERROR|quickSearch|com.Xxx.service.UploadFileService.dealQuickSearch:360|
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException:
### Error querying database.  Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection; nested exception is java.sql.SQLException: HikariDataSource HikariDataSource (null) has been closed.
### The error may exist in com/XXX/service/mapper/UploadFileDao.java (best guess)
### The error may involve com.Xxxx.mapper.UploadFileDao.selectList
### The error occurred while executing a query
### Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection; nested exception is java.sql.SQLException: HikariDataSource HikariDataSource (null) has been closed.

原因:因为我的service实现SmartInitializingSingleton这个类,在afterSingletonsInstantiated方法中启动一个后台处理线程,是一个后台处理任务的死循环的线程。这个线程里面会有数据库的查询。

猜测的原因,这个问题比较明显,应该是启动springboot的时候,web服务的端口1666已经被占用,启动失败。但是由于后台起了一个死循环的线程,导致整个进程无法因为结束(进程一直在)。这个占用1666端口的进程,其实也是同一个jar包启动的,导致从表面上看,web服务的功能是正常的,但是不停的输出错误日志(因为后台线程的死循环里一直查询数据库)。而这个报错又比较奇怪。后来直接通过ps命令查看进程,发现确实存在多个进程。

问题复现:先将多个进程都kill,然后手动启动一个进程,完全正常;再使用同样的命令启动,结果就出现了上面的错误日志。

解决方法:思路是(1)能否判断springweb启动成功以后,再启动后台线程?(2)能否通过判断springweb启动失败,去关闭该线程?

(2)基于2的思路先尝试了一下,因为看到如下错误日志:

复制代码
|ERROR|main|org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter.report:40|

我就想着看看LoggingFailureAnalysisReporter是如何打印错误日志的,结果发现它实现了一个FailureAnalysisReporter接口,通过下面这个方法调用的(FailureAnalyzers类中):

复制代码
private boolean report(FailureAnalysis analysis, ClassLoader classLoader) {
		List<FailureAnalysisReporter> reporters = SpringFactoriesLoader.loadFactories(FailureAnalysisReporter.class,
				classLoader);
		if (analysis == null || reporters.isEmpty()) {
			return false;
		}
		for (FailureAnalysisReporter reporter : reporters) {
			reporter.report(analysis);
		}
		return true;
	}

然后我自然而然就想到自己执行死循环的service也实现这个接口,在report的时候,设置一个本地变量,表示springweb启动失败,然后停止这个线程。

复制代码
@Override
    public void afterSingletonsInstantiated() {
        // 启动后台处理线程
        final Thread thread = new Thread(() -> {
            try {
                autoUpdate();
            } catch (Exception e) {
                log.error("处理的线程出现错误", e);
            }
        });
        thread.setName("quickSearch");
        thread.start();
    }

    // 启动失败的时候,设置为false
    @Override
    public void report(FailureAnalysis analysis) {
        this.isWebStartSuccess = false;
    }
    // 状态变量
    private volatile boolean isWebStartSuccess = true;

    // 死循环的线程
    public void autoUpdate() {
        while(true) {
            if (!isWebStartSuccess) {
                return;
            }
        }
    }

结果发现这个report方法跟本没被调用(原因下一篇再分析)。

然后我就采用了思路(1),问题就变成了:怎么能在springweb启动后再启动某个线程呢?

我再往上查了一下,有人采用在springboot启动类的main方法的第一行后面启动线程。我先再该行代码出增加了一行日志,发现如果端口冲突,springweb启动失败,这一行日志确实没有输出,然后就将这个死循环的线程,放在这里启动,主要代码就变成了:

复制代码
public static void main(String[] args) {
        SpringApplication.run(XxxxApplication.class, args);
        startAutoUpdateThread();
    }


    public static void startAutoUpdateThread() {
        final UploadFileService bean = SpringApplicationContext.getBean(UploadFileService.class);
        new Thread(() -> bean.autoUpdate()).start();
    }

这样处理以后,发现问题是解决了。但是不知道这样是否可能造成其他问题。

相关推荐
海山数据库1 小时前
移动云大云海山数据库(He3DB)postgresql_anonymizer插件原理介绍与安装
数据库·he3db·大云海山数据库·移动云数据库
云飞云共享云桌面1 小时前
高性能图形工作站的资源如何共享给10个SolidWorks研发设计用
linux·运维·服务器·前端·网络·数据库·人工智能
码农阿豪2 小时前
Flask应用上下文问题解析与解决方案:从错误日志到完美修复
后端·python·flask
2501_927993532 小时前
SQL Server 2022安装详细教程(图文详解,非常详细)
数据库·sqlserver
星火s漫天2 小时前
第一篇: 使用Docker部署flask项目(Flask + DB 容器化)
数据库·docker·flask
xcLeigh2 小时前
Python 项目实战:用 Flask 实现 MySQL 数据库增删改查 API
数据库·python·mysql·flask·教程·python3
威迪斯特2 小时前
Flask:轻量级Web框架的技术本质与工程实践
前端·数据库·后端·python·flask·开发框架·核心架构
xu_yule2 小时前
Redis存储(15)Redis的应用_分布式锁_Lua脚本/Redlock算法
数据库·redis·分布式
一灰灰blog2 小时前
Spring AI中的多轮对话艺术:让大模型主动提问获取明确需求
数据库·人工智能·spring
毕设源码-钟学长2 小时前
【开题答辩全过程】以 基于Springboot的扶贫众筹平台为例,包含答辩的问题和答案
java·spring boot·后端