shell脚本判断服务是否启动完成多次改进过程

背景

我们的服务专为金融领域的客户量身定制,分为多个独立而协同的模块。在这些模块中,Biz模块扮演着枢纽的角色,它不仅承担着对外提供接口的重要职责,而且还负责建立和维护与Mysql数据库的关键连接。其他所有模块都依赖于Biz模块。

为了实现自动化部署,我们编写了一套启动脚本。其中,validStart.sh脚本被设计用于判断服务是否成功启动。在第一版中,我们通过检查服务进程是否存在来判断服务是否启动完成,具体方法是使用ps命令加上pid参数来查询进程信息。

在客户实际使用中,我们发现了一些问题:

    1. 服务实际上并未启动完成时,系统却提示服务已经成功启动。
    1. 在多节点部署的情况下,由于服务尚未启动完成,其他服务就开始启动了,导致其他服务的请求被转移到了一个正在启动的节点中。

这些问题可能会影响客户的使用体验和系统的可靠性。因此,我们需要进一步优化validStart.sh脚本,以确保服务启动的准确性和稳定性。

实现方式

第一版

在第一版中我们在启动脚本中通过$!命令将pid记录到pidFile文件中,在validStart.sh脚本中轮训多次判断pid是否存在,用于判断服务是否启动成功。

源码如下:

bash 复制代码
#!/bin/bash

pidFile="biz-server.pid"
pidTmpFile="biz-server.tmp.pid"

function validate(){
    if [ "${3}" == "false" ]; then
      return 1
    fi
    pidFile=$1
    totalTime=$2
    while [[ $totalTime -gt 0 ]]; do
        if [ -f "$pidFile" ]; then
            sleep 5
            let totalTime-=5
            pid=`head -n +1 $pidFile`
            check_start=`ps axu | grep java | grep -w $pid |grep -v grep| awk '{printf $2}'`
            if [[ -n "$check_start" && "$pid" == "$check_start" ]]; then
                return 0
            else
                return 1
            fi
        fi
        let totalTime-=5
        sleep 5
    done
    return 1
}
sumTime=120
validate ${pidFile} $sumTime

if [ "$?" == "0" ];then
    echo "0"
else
    #启动失败后,需要看一下是否有临时pid文件,需要把该残留的进程也删除掉
    if [ -f "$pidTmpFile" ];then
        PID=$(cat ${pidTmpFile})
                alias rm='rm'
        kill -9 $PID 2>&1
        rm -rf $pidTmpFile 2>&1
    fi
    echo "start failed." 1>&2
fi

第二版本

在第二版本中,我们可以通过发送HTTP请求到服务的health接口来判断服务是否完全启动完成。具体来说,我们可以使用curl命令发送一个GET请求到health接口的URL,并检查响应内容来确定服务是否已经启动成功。

源码如下:

bash 复制代码
#!/bin/bash

# 声明接口地址
url=http://127.0.0.1:7082/health
# 添加服务状态检查方法
checkHealth(){
  totalTime=$1
  while [[ $totalTime -gt 0 ]]; do
       sleep 5
       result=$(curl -s ${url} | grep "\\\"status\\\": \\\"UP\\\"")
       echo $result
       if [ "$result" != "" ]; then
        return "1"
       else
        return "0"
       fi
       let totalTime-=5
       sleep 5
      done
      return 1
	

}

sumTime=120

checkHealth $sumTime

if [ "$?" == "0" ];then
    echo "0"
else
    #启动失败后,需要看一下是否有临时pid文件,需要把该残留的进程也删除掉
    if [ -f "$pidTmpFile" ];then
        PID=$(cat ${pidTmpFile})
		alias rm='rm'
        kill -9 $PID 2>&1
        rm -rf $pidTmpFile 2>&1
    fi
    echo "start failed." 1>&2
fi

第三版本

在第二个版本中,我们使用health接口来判断服务是否启动完成。然而,在实际使用中,我们发现即使health接口能够调用成功,服务并不一定能够正常提供服务,这主要是由于组件尚未注册好的原因。

为了解决这个问题,我们需要对health接口进行改造添加自定义的监控项进去。添加方法参考Spring Boot Actuator自定义监控集成到/health接口实战

查看Spring源码知道Spring提供了一个SpringApplicationRunListener接口用于监控启动的各个阶段。

以上方法分别是:

  • contextLoaded:在加载应用程序上下文后调用,但在加载之前调用刷新。
  • contextPrepared:创建并准备ApplicationContext后调用,但在加载源之前。
  • environmentPrepared:在准备好环境后,但在创建ApplicationContext之前调用。
  • failed:在运行应用程序时发生故障时调用。
  • running:在刷新应用程序上下文并且调用了所有CommandLineRunner和ApplicationRunner之后,在运行方法完成之前立即调用。
  • started:上下文已刷新,应用程序已启动,但尚未调用CommandLineRunners和ApplicationRunners。
  • starting:当run方法首次启动时立即调用。

而SpringBoot默认实现了一个EventPublishingRunListener监听器,并在running阶段会发布ApplicationReadyEvent事件。所以我们只需要监听这个事件就可以判断服务是否完全启动完成。

创建ApplicationReadyListener类继承ApplicationListener监听ApplicationReadyEvent事件,并定义一个全局变量readFlag用于记录服务状态。

源码如下:

java 复制代码
@Component
public class JresApplicationReadyListener implements ApplicationListener<ApplicationReadyEvent> {
    private static boolean readFlag=false;


    /**
     * 获取获取初始化状态
     * @return
     */
    public static boolean getReadyStatus(){
        return readFlag;
    }

    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        readFlag=true;
    }
}

然后自定义一个HealthCheck判断服务状态。

相关推荐
新手小袁_J2 分钟前
JDK11下载安装和配置超详细过程
java·spring cloud·jdk·maven·mybatis·jdk11
呆呆小雅3 分钟前
C#关键字volatile
java·redis·c#
Monly214 分钟前
Java(若依):修改Tomcat的版本
java·开发语言·tomcat
Ttang236 分钟前
Tomcat原理(6)——tomcat完整实现
java·tomcat
goTsHgo7 分钟前
在 Spring Boot 的 MVC 框架中 路径匹配的实现 详解
spring boot·后端·mvc
钱多多_qdd17 分钟前
spring cache源码解析(四)——从@EnableCaching开始来阅读源码
java·spring boot·spring
waicsdn_haha19 分钟前
Java/JDK下载、安装及环境配置超详细教程【Windows10、macOS和Linux图文详解】
java·运维·服务器·开发语言·windows·后端·jdk
飞的肖27 分钟前
前端使用 Element Plus架构vue3.0实现图片拖拉拽,后等比压缩,上传到Spring Boot后端
前端·spring boot·架构
Q_192849990629 分钟前
基于Spring Boot的摄影器材租赁回收系统
java·spring boot·后端
Code_流苏31 分钟前
VSCode搭建Java开发环境 2024保姆级安装教程(Java环境搭建+VSCode安装+运行测试+背景图设置)
java·ide·vscode·搭建·java开发环境