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判断服务状态。

相关推荐
成富6 分钟前
文本转SQL(Text-to-SQL),场景介绍与 Spring AI 实现
数据库·人工智能·sql·spring·oracle
计算机学长felix10 分钟前
基于SpringBoot的“校园交友网站”的设计与实现(源码+数据库+文档+PPT)
数据库·spring boot·毕业设计·交友
Re.不晚13 分钟前
Java入门15——抽象类
java·开发语言·学习·算法·intellij-idea
雷神乐乐19 分钟前
Maven学习——创建Maven的Java和Web工程,并运行在Tomcat上
java·maven
码农派大星。23 分钟前
Spring Boot 配置文件
java·spring boot·后端
顾北川_野30 分钟前
Android 手机设备的OEM-unlock解锁 和 adb push文件
android·java
江深竹静,一苇以航32 分钟前
springboot3项目整合Mybatis-plus启动项目报错:Invalid bean definition with name ‘xxxMapper‘
java·spring boot
confiself1 小时前
大模型系列——LLAMA-O1 复刻代码解读
java·开发语言
Wlq04151 小时前
J2EE平台
java·java-ee
XiaoLeisj1 小时前
【JavaEE初阶 — 多线程】Thread类的方法&线程生命周期
java·开发语言·java-ee