java项目启动脚本

app-server.sh

这里写目录标题

#!/bin/bashsource /etc/profile
source ~/.bash_profilecygwin=false
darwin=false
os400=false
case "$(uname)" in
CYGWIN*) cygwin=true ;;
Darwin*) darwin=true ;;
OS400*) os400=true ;;
esacerror_exit() {
echo "ERROR: $1 !!"
exit 1
}#===========================================================================================
# 准备Java运行环境
#===========================================================================================[ ! -e "$JAVA_HOME/bin/java" ] && JAVA_HOME=$HOME/jdk/java
[ ! -e "$JAVA_HOME/bin/java" ] && JAVA_HOME=/usr
[ ! -e "$JAVA_HOME/bin/java" ] && JAVA_HOME=/usr/java
[ ! -e "$JAVA_HOME/bin/java" ] && JAVA_HOME=/opt/taobao/java
[ ! -e "$JAVA_HOME/bin/java" ] && unset JAVA_HOMEif [ -z "$JAVA_HOME" ]; then
if $darwin; then
if [ -x '/usr/libexec/java_home' ]; then
export JAVA_HOME=$(/usr/libexec/java_home)elif [ -d "/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home" ]; then
export JAVA_HOME="/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home"
fi
else
JAVA_PATH=$(dirname $(readlink -f $(which javac)))
if [ "x$JAVA_PATH" != "x" ]; then
export JAVA_HOME=$(dirname $JAVA_PATH 2>/dev/null)
fi
fi
if [ -z "$JAVA_HOME" ]; then
error_exit "Please set the JAVA_HOME variable in your environment, We need java(x64)! jdk8 or later is better!"
fi
fi
export JAVA="$JAVA_HOME/bin/java"#===========================================================================================
# Service Configuration
#===========================================================================================
[ ! -z ${2} ] && BASE_DIR=${2} || BASE_DIR=$(
cd $(dirname $0)
pwd
)
cd ${BASE_DIR}
pwd
APP_NAME=quantum-im-account
APP_JAR=${BASE_DIR}/${APP_NAME}\.jar
APP_CONF_PATH=${BASE_DIR}/config
APP_LOGS_PATH=${BASE_DIR}/logs
APP_LIB_PATH=${BASE_DIR}/lib
APP_PID=${BASE_DIR}/${APP_NAME}.pid
echo "BASE_DIR => ${BASE_DIR}"[ ! -d "${APP_LOGS_PATH}" ] && mkdir -p ${APP_LOGS_PATH}#===========================================================================================
# JVM Configuration
#===========================================================================================
JAVA_OPTS="$JAVA_OPTS -Xss256k -Xms1g -Xmx1g "
JAVA_OPTS="$JAVA_OPTS -XX:+DisableExplicitGC -XX:ParallelGCThreads=16 -XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC -XX:+UseParNewGC"
# -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=$CATALINA_BASE/logs
JAVA_OPTS="$JAVA_OPTS -XX:CMSFullGCsBeforeCompaction=5 -XX:CMSInitiatingOccupancyFraction=80 -XX:MaxTenuringThreshold=15 "
JAVA_OPTS="$JAVA_OPTS -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintSafepointStatistics -XX:+PrintTenuringDistribution -XX:+PrintHeapAtGC -Xloggc:$APP_LOGS_PATH/gc.log "
JAVA_OPTS="$JAVA_OPTS -Dclient.enczoding.override=UTF-8 -Dfile.encoding=UTF-8 -Duser.language=zh -Duser.region=CN -Dspring.cloud.nacos.discovery.namespace=quantum-im-test"
#JAVA_OPTS="$JAVA_OPTS -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=6001"
JAVA_OPTS="$JAVA_OPTS -Xbootclasspath/a:${APP_CONF_PATH}"
#链路追踪
JAVA_OPTS="$JAVA_OPTS -javaagent:${APP_LIB_PATH}/opentelemetry-javaagent.jar "
JAVA_OPTS="$JAVA_OPTS -Dotel.traces.exporter=logging -Dotel.metrics.exporter=none -Dotel.logs.exporter=none -Dotel.metric.export.interval=15000"
JAVA_CMD="${JAVA} ${JAVA_OPTS} -jar ${APP_JAR}"
echo "${JAVA_CMD}"#===========================================================================================
# 命令行
#===========================================================================================
case $1 in
"start")
if [ ! -f ${APP_PID} ]; then
echo "[start] Starting ${APP_NAME} ..."
nohup ${JAVA_CMD} >/dev/null 2>&1 &echo $! >${APP_PID}echo "[start] ${APP_NAME} started ..."elseecho "[start] ${APP_NAME} is already running ..."fi;;
"run")if [ ! -f ${APP_PID} ]; thenecho "[run] Starting ${APP_NAME} ..."${JAVA_CMD}echo "[run] ${APP_NAME} started ..."elseecho "[run] ${APP_NAME} is already running ..."fi;;
"stop")if [ -f ${APP_PID} ]; thenecho "[stop] ${APP_NAME} stoping ..."kill -2 $(cat ${APP_PID})rm -rf ${APP_PID}ps -ef | grep ${APP_NAME}.jar | grep -v grep | awk '{print $2}' | xargs kill -9echo "[stop] ${APP_NAME} stopped ..."elseecho "[stop] ${APP_NAME} is not running ..."fi;;
"restart")echo ${APP_PID}if [ -f ${APP_PID} ]; thenecho "[restart] ${APP_NAME} stopping ..."kill -2 $(cat ${APP_PID})rm -rf ${APP_PID}ps -ef | grep ${APP_NAME}.jar | grep -v grep | awk '{print $2}' | xargs kill -9echo "[restart] ${APP_NAME} starting ..."nohup ${JAVA_CMD} >/dev/null 2>&1 &echo $! >${APP_PID}echo "[restart] ${APP_NAME} started ..."elseecho "[restart] ${APP_NAME} is not running, Starting ${APP_NAME} ..."nohup ${JAVA_CMD} >/dev/null 2>&1 &echo $! >${APP_PID}echo "[restart] ${APP_NAME} started ..."fi;;
esac

1.准备Java运行环境

[ ! -e "$JAVA_HOME/bin/java" ] && JAVA_HOME=$HOME/jdk/java
[ ! -e "$JAVA_HOME/bin/java" ] && JAVA_HOME=/usr
[ ! -e "$JAVA_HOME/bin/java" ] && JAVA_HOME=/usr/java
[ ! -e "$JAVA_HOME/bin/java" ] && JAVA_HOME=/opt/taobao/java
[ ! -e "$JAVA_HOME/bin/java" ] && unset JAVA_HOMEif [ -z "$JAVA_HOME" ]; thenif $darwin; thenif [ -x '/usr/libexec/java_home' ]; thenexport JAVA_HOME=$(/usr/libexec/java_home)elif [ -d "/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home" ]; thenexport JAVA_HOME="/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home"fielseJAVA_PATH=$(dirname $(readlink -f $(which javac)))if [ "x$JAVA_PATH" != "x" ]; thenexport JAVA_HOME=$(dirname $JAVA_PATH 2>/dev/null)fifiif [ -z "$JAVA_HOME" ]; thenerror_exit "Please set the JAVA_HOME variable in your environment, We need java(x64)! jdk8 or later is better!"fi
fi
export JAVA="$JAVA_HOME/bin/java"

分析

第一部分

这段代码是一个Bash脚本片段,用于设置或更新JAVA_HOME环境变量,以确保JAVA_HOME指向一个有效的Java安装路径。以下是代码的逐步解析和解释:

代码解析
  1. **[ ! -e "$JAVA_HOME/bin/java" ]**
    这是一个条件判断语句,检查$JAVA_HOME/bin/java文件是否存在。-e是一个测试操作符,用于检查文件是否存在。
  2. **&&**
    这是一个逻辑“与”操作符,表示只有当前面的条件为真(即$JAVA_HOME/bin/java不存在)时,才会执行后面的命令。
  3. **JAVA_HOME=$HOME/jdk/java**
    如果$JAVA_HOME/bin/java不存在,则将JAVA_HOME设置为$HOME/jdk/java$HOME是一个环境变量,表示当前用户的主目录。
  4. 后续的**[ ! -e "$JAVA_HOME/bin/java" ]****JAVA_HOME**赋值语句
    这些语句依次尝试不同的路径,直到找到一个有效的java可执行文件路径。如果某个路径有效,则JAVA_HOME会被设置为该路径。
  5. **unset JAVA_HOME**
    如果所有尝试的路径都不包含java可执行文件,则最终会执行unset JAVA_HOME,将JAVA_HOME环境变量清除。
代码逻辑

这段代码的逻辑是:

  1. 检查当前$JAVA_HOME/bin/java是否存在。
  2. 如果不存在,则依次尝试以下路径:
    • $HOME/jdk/java
    • /usr
    • /usr/java
    • /opt/taobao/java
  3. 如果以上路径都无效,则清除JAVA_HOME变量。
示例运行

假设初始时JAVA_HOME未设置,或者$JAVA_HOME/bin/java不存在,脚本会依次尝试以下路径:

  1. $HOME/jdk/java/bin/java
    如果存在,则JAVA_HOME被设置为$HOME/jdk/java
  2. 如果不存在,尝试/usr/bin/java。如果存在,则JAVA_HOME被设置为/usr
  3. 如果仍然不存在,尝试/usr/java/bin/java。如果存在,则JAVA_HOME被设置为/usr/java
  4. 如果仍然不存在,尝试/opt/taobao/java/bin/java。如果存在,则JAVA_HOME被设置为/opt/taobao/java
  5. 如果所有路径都无效,最终执行unset JAVA_HOME,清除JAVA_HOME变量。
注意事项
  1. 路径顺序
    路径的顺序很重要。脚本会依次尝试每个路径,直到找到有效的java可执行文件。如果某个路径有效,后续的路径将被忽略。
  2. 环境变量的持久性
    这段脚本通常用于脚本文件中(如bash脚本或profile文件)。如果在命令行中直接运行,JAVA_HOME的设置只对当前终端会话有效。如果希望永久生效,需要将这段代码添加到~/.bashrc~/.bash_profile/etc/profile等文件中。
  3. 多Java版本
    如果系统中安装了多个Java版本,这段脚本会设置第一个有效的路径。如果需要切换Java版本,可以手动设置JAVA_HOME,或者使用工具如update-alternatives(在Linux系统中)。
总结

这段代码是一个实用的脚本片段,用于动态设置JAVA_HOME环境变量,确保其指向一个有效的Java安装路径。它通过依次尝试多个可能的路径,避免了因JAVA_HOME设置错误而导致的问题。

第二部分

这段脚本的主要目的是确保JAVA_HOME环境变量被正确设置,并且最终设置JAVA变量为$JAVA_HOME/bin/java。它根据不同的操作系统(主要是区分是否为macOS)采取不同的策略来查找Java的安装路径。以下是详细解析:

1. 检查JAVA_HOME是否已设置

bash复制

if [ -z "$JAVA_HOME" ]; then
  • 如果JAVA_HOME变量为空(-z表示变量为空),则进入条件块。
2. macOS环境下的Java路径查找

bash复制

if $darwin; then
  • $darwin是一个变量,假设它是一个布尔值(通常是通过某种方式设置的,例如darwin=$(uname -s | grep -i darwin)),用于判断是否为macOS系统。
  • 如果是macOS系统,则尝试以下两种方法来设置JAVA_HOME
方法1:使用/usr/libexec/java_home

bash复制

if [ -x '/usr/libexec/java_home' ]; thenexport JAVA_HOME=$(/usr/libexec/java_home)
  • 检查/usr/libexec/java_home是否存在且可执行(-x表示文件存在且可执行)。
  • 如果存在,则调用/usr/libexec/java_home命令来获取Java的安装路径,并将其赋值给JAVA_HOME
  • /usr/libexec/java_home是macOS提供的一个工具,用于获取当前默认的Java安装路径。
方法2:使用系统框架路径

bash复制

elif [ -d "/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home" ]; thenexport JAVA_HOME="/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home"
  • 如果/usr/libexec/java_home不存在,则检查/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home目录是否存在。
  • 如果存在,则直接将该路径赋值给JAVA_HOME
  • 这是macOS早期版本中Java安装路径的一种方式。
3. 非macOS环境下的Java路径查找

bash复制

else
  • 如果不是macOS系统(假设是Linux或Unix系统),则尝试以下方法:
方法:通过javac查找

bash复制

JAVA_PATH=$(dirname $(readlink -f $(which javac)))if [ "x$JAVA_PATH" != "x" ]; thenexport JAVA_HOME=$(dirname $JAVA_PATH 2>/dev/null)fi
  • 通过which javac找到javac的路径。
  • 使用readlink -f获取javac的实际路径(解决符号链接问题)。
  • 使用dirname获取javac所在的目录(通常是bin目录)。
  • 再次使用dirname获取JAVA_HOME路径(即bin目录的父目录)。
  • 如果JAVA_PATH为空,则跳过设置JAVA_HOME
4. 如果JAVA_HOME仍然未设置

bash复制

if [ -z "$JAVA_HOME" ]; thenerror_exit "Please set the JAVA_HOME variable in your environment, We need java(x64)! jdk8 or later is better!"fi
  • 如果经过上述尝试后,JAVA_HOME仍然为空,则调用error_exit函数(假设这是一个自定义的错误处理函数),提示用户设置JAVA_HOME
  • 要求用户安装64位Java,并建议使用JDK 8或更高版本。
5. 设置JAVA变量

bash复制

export JAVA="$JAVA_HOME/bin/java"
  • 最后,将JAVA_HOME/bin/java赋值给JAVA变量,并导出为环境变量。
  • 这样,JAVA变量可以用于后续脚本中调用Java命令。
总结

这段脚本的主要功能是:

  1. 检查JAVA_HOME是否已设置。
  2. 如果未设置,则根据操作系统类型(macOS或非macOS)尝试自动查找Java的安装路径。
  3. 如果找到有效的路径,则设置JAVA_HOME
  4. 如果未找到有效的路径,则提示用户手动设置JAVA_HOME
  5. 最终设置JAVA变量为$JAVA_HOME/bin/java,以便后续使用。

这种脚本在跨平台的Java应用部署中非常有用,可以自动适配不同操作系统的Java环境。

2.Service Configuration

[ ! -z ${2} ] && BASE_DIR=${2} || BASE_DIR=$(cd $(dirname $0)pwd
)
cd ${BASE_DIR}
pwd
APP_NAME=testApp
APP_JAR=${BASE_DIR}/${APP_NAME}\.jar
APP_CONF_PATH=${BASE_DIR}/config
APP_LOGS_PATH=${BASE_DIR}/logs
APP_LIB_PATH=${BASE_DIR}/lib
APP_PID=${BASE_DIR}/${APP_NAME}.pid
echo "BASE_DIR => ${BASE_DIR}"[ ! -d "${APP_LOGS_PATH}" ] && mkdir -p ${APP_LOGS_PATH}

这段脚本是一个典型的用于设置 Java 应用程序运行环境的 Shell 脚本。它的主要功能是确定应用程序的根目录(BASE_DIR),并根据这个目录设置相关的路径(如配置文件路径、日志路径、库路径等)。以下是逐行解析和解释:

脚本解析
1. 确定 BASE_DIR
[ ! -z ${2} ] && BASE_DIR=${2} || BASE_DIR=$(cd $(dirname $0)pwd
)
  • 功能:这段代码用于确定应用程序的根目录(BASE_DIR)。
    • 如果脚本的第二个参数(${2})不为空,则将 ${2} 的值赋给 BASE_DIR
    • 如果没有提供第二个参数,则通过 cd $(dirname $0)pwd 命令获取脚本所在的目录,并将其赋值给 BASE_DIR
  • 解释
    • $(dirname $0):获取脚本文件的目录路径。
    • pwd:打印当前工作目录的绝对路径。
    • [ ! -z ${2} ]:检查 ${2} 是否为空。如果不为空,则执行 && 后面的命令;否则执行 || 后面的命令。
2. 切换到 BASE_DIR
cd ${BASE_DIR}
pwd
  • 功能:切换到 BASE_DIR 目录,并打印当前工作目录。
  • 解释
    • cd ${BASE_DIR}:切换到 BASE_DIR 指定的目录。
    • pwd:打印当前工作目录,用于确认切换是否成功。
3. 设置相关路径
APP_NAME=testApp
APP_JAR=${BASE_DIR}/${APP_NAME}\.jar
APP_CONF_PATH=${BASE_DIR}/config
APP_LOGS_PATH=${BASE_DIR}/logs
APP_LIB_PATH=${BASE_DIR}/lib
APP_PID=${BASE_DIR}/${APP_NAME}.pid
echo "BASE_DIR => ${BASE_DIR}"
  • 功能:定义应用程序的相关路径。
    • APP_NAME:应用程序的名称。
    • APP_JAR:应用程序的 JAR 文件路径。
    • APP_CONF_PATH:配置文件路径。
    • APP_LOGS_PATH:日志文件路径。
    • APP_LIB_PATH:库文件路径。
    • APP_PID:用于存储应用程序进程 ID 的文件路径。
  • 解释
    • ${BASE_DIR}/${APP_NAME}\.jar:使用反斜杠 \ 对点号 . 进行转义,以确保路径中的点号不会被错误解析。
    • echo "BASE_DIR => ${BASE_DIR}":打印 BASE_DIR 的值,用于调试。
4. 创建日志目录
[ ! -d "${APP_LOGS_PATH}" ] && mkdir -p ${APP_LOGS_PATH}
  • 功能:检查日志目录是否存在,如果不存在则创建。
  • 解释
    • [ ! -d "${APP_LOGS_PATH}" ]:检查 ${APP_LOGS_PATH} 是否不是一个目录。
    • mkdir -p ${APP_LOGS_PATH}:创建目录及其所有父目录(如果不存在)。
脚本的作用

这段脚本的主要作用是:

  1. 确定应用程序的根目录(BASE_DIR)。
  2. 设置应用程序的 JAR 文件路径、配置文件路径、日志文件路径、库文件路径等。
  3. 确保日志目录存在(如果不存在则创建)。
示例运行

假设脚本文件名为 start.sh,并且脚本所在的目录为 /path/to/app,运行脚本时可以这样操作:

/path/to/app/start.sh arg1 arg2
  • 如果提供了第二个参数 arg2,则 BASE_DIR 会被设置为 arg2
  • 如果没有提供第二个参数,则 BASE_DIR 会被设置为脚本所在的目录 /path/to/app
注意事项
  1. 路径分隔符
    • 在 Linux 和 macOS 系统中,路径分隔符是 /
    • 如果你需要在 Windows 系统中运行类似的脚本,需要特别注意路径分隔符(Windows 使用 \)。
  2. 权限问题
    • 确保脚本有执行权限,可以通过 chmod +x start.sh 命令设置。
    • 确保脚本所在的目录和相关路径有适当的读写权限。
  3. 调试
    • 如果脚本运行时出现问题,可以通过在脚本中添加更多的 echo 命令来打印变量值,帮助定位问题。

3.JVM Configuration

从你的代码来看,你已经正确地构建了 JAVA_OPTSJAVA_CMD,用于启动一个 Java 应用程序。以下是对你代码的详细解析和一些建议,以确保它能够正常运行并达到预期的效果。

代码解析
1. JVM 内存和线程设置

bash复制

JAVA_OPTS="$JAVA_OPTS -Xss256k -Xms1g -Xmx1g"
  • **-Xss256k**:设置每个线程的堆栈大小为 256 KB。
  • **-Xms1g**:设置 JVM 的初始堆内存大小为 1 GB。
  • **-Xmx1g**:设置 JVM 的最大堆内存大小为 1 GB。
2. 垃圾回收和性能优化

bash复制

JAVA_OPTS="$JAVA_OPTS -XX:+DisableExplicitGC -XX:ParallelGCThreads=16 -XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC -XX:+UseParNewGC"
  • **-XX:+DisableExplicitGC**:禁用显式垃圾回收(System.gc())。
  • **-XX:ParallelGCThreads=16**:设置并行垃圾回收线程数为 16。
  • **-XX:+CMSClassUnloadingEnabled**:启用 CMS 垃圾回收器的类卸载功能。
  • **-XX:+UseConcMarkSweepGC**:使用 CMS 垃圾回收器。
  • **-XX:+UseParNewGC**:使用 ParNew 垃圾回收器作为新生代回收器。
3. 垃圾回收调优

bash复制

JAVA_OPTS="$JAVA_OPTS -XX:CMSFullGCsBeforeCompaction=5 -XX:CMSInitiatingOccupancyFraction=80 -XX:MaxTenuringThreshold=15"
  • **-XX:CMSFullGCsBeforeCompaction=5**:在进行 5 次 Full GC 后进行一次内存整理。
  • **-XX:CMSInitiatingOccupancyFraction=80**:当老年代占用率达到 80% 时触发 CMS 垃圾回收。
  • **-XX:MaxTenuringThreshold=15**:设置对象在新生代中存活的最大次数为 15 次。
4. 垃圾回收日志

bash复制

JAVA_OPTS="$JAVA_OPTS -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintSafepointStatistics -XX:+PrintTenuringDistribution -XX:+PrintHeapAtGC -Xloggc:$APP_LOGS_PATH/gc.log"
  • **-XX:+PrintGCDetails**:打印详细的垃圾回收信息。
  • **-XX:+PrintGCDateStamps**:打印垃圾回收的时间戳。
  • **-XX:+PrintSafepointStatistics**:打印安全点(Safepoint)的统计信息。
  • **-XX:+PrintTenuringDistribution**:打印对象在新生代中的存活次数分布。
  • **-XX:+PrintHeapAtGC**:在每次垃圾回收时打印堆内存的使用情况。
  • **-Xloggc:$APP_LOGS_PATH/gc.log**:将垃圾回收日志输出到指定的文件。
5. 国际化和编码

bash复制

JAVA_OPTS="$JAVA_OPTS -Dclient.encoding.override=UTF-8 -Dfile.encoding=UTF-8 -Duser.language=zh -Duser.region=CN -Dspring.cloud.nacos.discovery.namespace=quantum-im-test"
  • **-Dclient.encoding.override=UTF-8**:设置客户端编码为 UTF-8。
  • **-Dfile.encoding=UTF-8**:设置文件默认编码为 UTF-8。
  • **-Duser.language=zh**:设置用户语言为中文。
  • **-Duser.region=CN**:设置用户区域为中国。
  • **-Dspring.cloud.nacos.discovery.namespace=quantum-im-test**:设置 Nacos 服务发现的命名空间。
6. 引导类路径

bash复制

JAVA_OPTS="$JAVA_OPTS -Xbootclasspath/a:${APP_CONF_PATH}"
  • **-Xbootclasspath/a:${APP_CONF_PATH}**:将 ${APP_CONF_PATH} 追加到引导类路径的末尾。
7. 链路追踪

bash复制

JAVA_OPTS="$JAVA_OPTS -javaagent:${APP_LIB_PATH}/opentelemetry-javaagent.jar"
JAVA_OPTS="$JAVA_OPTS -Dotel.traces.exporter=logging -Dotel.metrics.exporter=none -Dotel.logs.exporter=none -Dotel.metric.export.interval=15000"
  • **-javaagent:${APP_LIB_PATH}/opentelemetry-javaagent.jar**:使用 OpenTelemetry Java Agent 进行链路追踪。
  • **-Dotel.traces.exporter=logging**:将链路追踪数据输出到日志。
  • **-Dotel.metrics.exporter=none**:禁用指标数据的导出。
  • **-Dotel.logs.exporter=none**:禁用日志数据的导出。
  • **-Dotel.metric.export.interval=15000**:设置指标数据导出的时间间隔为 15 秒。
8. 构建 Java 命令

bash复制

JAVA_CMD="${JAVA} ${JAVA_OPTS} -jar ${APP_JAR}"
echo "${JAVA_CMD}"
  • **${JAVA} ${JAVA_OPTS} -jar ${APP_JAR}**:构建完整的 Java 命令。
  • **echo "${JAVA_CMD}"**:打印完整的 Java 命令,用于调试。
注意事项
1. 垃圾回收器的选择
  • 你仍然使用了 CMS 垃圾回收器,但 CMS 已经被标记为废弃(在 Java 9 中)。建议使用 G1 垃圾回收器(-XX:+UseG1GC)或 ZGC(-XX:+UseZGC),以获得更好的性能和可维护性。
2. 日志路径
  • 确保 ${APP_LOGS_PATH} 已正确设置,并且目录存在。如果目录不存在,可以通过脚本创建:

bash复制

[ ! -d "${APP_LOGS_PATH}" ] && mkdir -p ${APP_LOGS_PATH}
3. 链路追踪
  • 确保 ${APP_LIB_PATH}/opentelemetry-javaagent.jar 文件存在。如果文件不存在,会导致 Java 命令启动失败。
4. 调试
  • 如果需要启用远程调试,可以取消注释 -agentlib:jdwp 的行:

bash复制

JAVA_OPTS="$JAVA_OPTS -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=6001"
示例输出

假设以下变量已正确设置:

bash复制

JAVA=/usr/bin/java
BASE_DIR=/path/to/app
APP_NAME=testApp
APP_JAR=/path/to/app/testApp.jar
APP_LOGS_PATH=/path/to/app/logs
APP_CONF_PATH=/path/to/app/config
APP_LIB_PATH=/path/to/app/lib

运行脚本后,echo "${JAVA_CMD}" 的输出可能是:

bash复制

/usr/bin/java -Xss256k -Xms1g -Xmx1g -XX:+DisableExplicitGC -XX:ParallelGCThreads=16 -XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:CMSFullGCsBeforeCompaction=5 -XX:CMSInitiatingOccupancyFraction=80 -XX:MaxTenuringThreshold=15 -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintSafepointStatistics -XX:+PrintTenuringDistribution -XX:+PrintHeapAtGC -Xloggc:/path/to/app/logs/gc.log -Dclient.encoding.override=UTF-8 -Dfile.encoding=UTF-8 -Duser.language=zh -Duser.region=CN -Dspring.cloud.nacos.discovery.namespace=quantum-im-test -Xbootclasspath/a:/path/to/app/config -javaagent:/path/to/app/lib/opentelemetry-javaagent.jar -Dotel.traces.exporter=logging -Dotel.metrics.exporter=none -Dotel.logs.exporter=none -Dotel.metric.export.interval=15000 -jar /path/to/app/quantum-im-account.jar

4.命令行

这段脚本是一个完整的用于管理 Java 应用程序的启动、运行、停止和重启的 Shell 脚本。它通过 case 语句根据传入的第一个参数($1)来执行不同的操作。以下是对这段脚本的详细解析和一些改进建议。

1. 启动应用(start

bash复制

"start")if [ ! -f ${APP_PID} ]; thenecho "[start] Starting ${APP_NAME} ..."nohup ${JAVA_CMD} >/dev/null 2>&1 &echo $! >${APP_PID}echo "[start] ${APP_NAME} started ..."elseecho "[start] ${APP_NAME} is already running ..."fi;;
  • 功能
    • 检查是否存在 ${APP_PID} 文件。如果不存在,表示应用未运行。
    • 使用 nohup 启动 Java 应用程序,将标准输出和标准错误重定向到 /dev/null,并将其放到后台运行。
    • 将启动的进程 ID 写入 ${APP_PID} 文件。
  • 注意
    • nohup>/dev/null 2>&1 & 确保应用在后台运行,且不会在终端关闭时停止。
    • 如果 ${APP_PID} 文件已存在,表示应用已在运行,不会重复启动。
2. 前台运行应用(run

bash复制

"run")if [ ! -f ${APP_PID} ]; thenecho "[run] Starting ${APP_NAME} ..."${JAVA_CMD}echo "[run] ${APP_NAME} started ..."elseecho "[run] ${APP_NAME} is already running ..."fi;;
  • 功能
    • 检查是否存在 ${APP_PID} 文件。如果不存在,表示应用未运行。
    • 在前台启动 Java 应用程序,用户可以看到程序的输出。
    • 如果 ${APP_PID} 文件已存在,表示应用已在运行,不会重复启动。
3. 停止应用(stop

bash复制

"stop")if [ -f ${APP_PID} ]; thenecho "[stop] ${APP_NAME} stoping ..."kill -2 $(cat ${APP_PID})rm -rf ${APP_PID}ps -ef | grep ${APP_NAME}.jar | grep -v grep | awk '{print $2}' | xargs kill -9echo "[stop] ${APP_NAME} stopped ..."elseecho "[stop] ${APP_NAME} is not running ..."fi;;
  • 功能
    • 检查是否存在 ${APP_PID} 文件。如果存在,表示应用正在运行。
    • 使用 kill -2 发送 SIGINT 信号给应用(通常用于优雅关闭)。
    • 删除 ${APP_PID} 文件。
    • 使用 ps -efgrep 查找所有与 ${APP_NAME}.jar 相关的进程,并使用 kill -9 强制终止这些进程。
  • 注意
    • kill -2 是发送 SIGINT 信号,类似于按下 Ctrl+C
    • kill -9 是强制终止进程,通常用于确保进程完全停止。
    • 如果 ${APP_PID} 文件不存在,表示应用未运行,不会执行任何操作。
4. 重启应用(restart

bash复制

"restart")echo ${APP_PID}if [ -f ${APP_PID} ]; thenecho "[restart] ${APP_NAME} stopping ..."kill -2 $(cat ${APP_PID})rm -rf ${APP_PID}ps -ef | grep ${APP_NAME}.jar | grep -v grep | awk '{print $2}' | xargs kill -9echo "[restart] ${APP_NAME} starting ..."nohup ${JAVA_CMD} >/dev/null 2>&1 &echo $! >${APP_PID}echo "[restart] ${APP_NAME} started ..."elseecho "[restart] ${APP_NAME} is not running, Starting ${APP_NAME} ..."nohup ${JAVA_CMD} >/dev/null 2>&1 &echo $! >${APP_PID}echo "[restart] ${APP_NAME} started ..."fi;;
  • 功能
    • 检查是否存在 ${APP_PID} 文件。如果存在,表示应用正在运行。
    • 停止应用(类似于 stop 操作)。
    • 启动应用(类似于 start 操作)。
    • 如果 ${APP_PID} 文件不存在,直接启动应用。
改进建议
1. 日志输出
  • 当前脚本将日志输出到 /dev/null,这意味着所有日志都会被丢弃。建议将日志输出到一个文件中,方便后续排查问题。

bash复制

nohup ${JAVA_CMD} >${APP_LOGS_PATH}/${APP_NAME}.log 2>&1 &
2. 优雅停止
  • 当前脚本使用 kill -2kill -9 来停止应用。虽然 kill -2 是优雅停止的信号,但 kill -9 是强制停止,可能会导致一些资源未释放或数据未保存。建议在发送 kill -9 之前,增加一个等待时间,确保应用有足够的时间优雅关闭。

bash复制

kill -2 $(cat ${APP_PID})
sleep 10  # 等待 10 秒
ps -ef | grep ${APP_NAME}.jar | grep -v grep | awk '{print $2}' | xargs kill -9
3. 检查应用是否真正停止
  • 在停止应用后,建议检查应用是否真正停止,如果没有停止,再执行 kill -9

bash复制

kill -2 $(cat ${APP_PID})
sleep 10
if ps -p $(cat ${APP_PID}) > /dev/null; thenecho "[stop] ${APP_NAME} did not stop gracefully, forcing termination ..."ps -ef | grep ${APP_NAME}.jar | grep -v grep | awk '{print $2}' | xargs kill -9
fi
4. 重启逻辑优化
  • 当前重启逻辑中,echo ${APP_PID} 是多余的,可以移除。
  • 重启逻辑可以复用 stopstart 的逻辑,避免重复代码。
优化后的脚本

以下是优化后的脚本:

bash复制

case $1 in
"start")if [ ! -f ${APP_PID} ]; thenecho "[start] Starting ${APP_NAME} ..."nohup ${JAVA_CMD} >${APP_LOGS_PATH}/${APP_NAME}.log 2>&1 &echo $! >${APP_PID}echo "[start] ${APP_NAME} started ..."elseecho "[start] ${APP_NAME} is already running ..."fi;;
"run")if [ ! -f ${APP_PID} ]; thenecho "[run] Starting ${APP_NAME} ..."${JAVA_CMD}echo "[run] ${APP_NAME} started ..."elseecho "[run] ${APP_NAME} is already running ..."fi;;
"stop")if [ -f ${APP_PID} ]; thenecho "[stop] ${APP_NAME} stopping ..."kill -2 $(cat ${APP_PID})sleep 10if ps -p $(cat ${APP_PID}) > /dev/null; thenecho "[stop] ${APP_NAME} did not stop gracefully, forcing termination ..."ps -ef | grep ${APP_NAME}.jar | grep -v grep | awk '{print $2}' | xargs kill -9firm -rf ${APP_PID}echo "[stop] ${APP_NAME} stopped ..."elseecho "[stop] ${APP_NAME} is not running ..."fi;;
"restart")echo "[restart] ${APP_NAME} restarting ..."$0 stop$0 start;;
*)echo "Usage: $0 {start|run|stop|restart}";;
esac

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/12961.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java高频面试之SE-17

hello啊,各位观众姥爷们!!!本牛马baby今天又来了!哈哈哈哈哈嗝🐶 Java缓冲区溢出,如何解决? 在 Java 中,缓冲区溢出 (Buffer Overflow) 虽然不是像 C/C 中那样直接可见…

用 Python 绘制爱心形状的简单教程

1. 引言 在本教程中,我们将学习如何使用 Python 和 Matplotlib 库来绘制一个简单的爱心形状。这是一个有趣且简单的项目,适合初学者练习图形绘制和数据可视化。 2. 环境准备 首先,确保您的系统上安装了 Python 和 Matplotlib 库。如果还未…

107,【7】buuctf web [CISCN2019 华北赛区 Day2 Web1]Hack World

这次先不进入靶场 看到红框里面的话就想先看看uuid是啥 定义与概念 UUID 是 Universally Unique Identifier 的缩写,即通用唯一识别码。它是一种由数字和字母组成的 128 位标识符,在理论上可以保证在全球范围内的唯一性。UUID 的设计目的是让分布式系…

Linux之安装MySQL

1、查看系统当前版本是多少位的 getconf LONG_BIT2.去官网下载对应的MYSQL安装包 这里下载的是8版本的,位数对应之前的64位 官网地址:https://downloads.mysql.com/archives/community/ 3.上传压缩包 4.到对应目录下解压 tar -xvf mysql-8.0.26-lin…

【NLP 20、Encoding编码 和 Embedding嵌入】

目录 一、核心定义与区别 二、常见Encoding编码 (1) 独热编码(One-Hot Encoding) (2) 位置编码(Positional Encoding) (3) 标签编码(Label Encoding) (4) 注意事项 三、常见Embedding词嵌入 (1) 基础词嵌入…

【ArcGIS Pro 简介1】

ArcGIS Pro 是由 Esri (Environmental Systems Research Institute)公司开发的下一代桌面地理信息系统(GIS)软件,是传统 ArcMap 的现代化替代产品。它结合了强大的空间分析能力、直观的用户界面和先进的三维可视化技术…

初学 Xvisor 之理解并跑通 Demo

官网:https://www.xhypervisor.org/ quick-start 文档:https://github.com/xvisor/xvisor/blob/master/docs/riscv/riscv64-qemu.txt 零、Xvisor 介绍 下面这部分是 Xvisor 官方的介绍 Xvisor 是一款开源的 Type-1 虚拟机管理程序,旨在提供一…

“AI智能分析综合管理系统:企业管理的智慧中枢

在如今这个快节奏的商业世界里,企业面临的挑战越来越多,数据像潮水一样涌来,管理工作变得愈发复杂。为了应对这些难题,AI智能分析综合管理系统闪亮登场,它就像是企业的智慧中枢,让管理变得轻松又高效。 过去…

LabVIEW涡轮诊断系统

一、项目背景与行业痛点 涡轮机械是发电厂、航空发动机、石油化工等领域的核心动力设备,其运行状态直接关系到生产安全与经济效益。据统计,涡轮故障导致的非计划停机可造成每小时数十万元的经济损失,且突发故障可能引发严重安全事故。传统人…

Hugging Face 的研究人员正致力于打造 OpenAI 深度研究工具的“开源版

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…

Java进阶(JVM调优)——阿里云的Arthas的使用 安装和使用 死锁查找案例,重新加载案例,慢调用分析

前言 JVM作为Java进阶的知识,是需要Java程序员不断深度和理解的。 本篇博客介绍JVM调优的工具阿里云的Arthas的使用,安装和使用,命令的使用案例;死锁查询的案例;重新加载一个类信息的案例;调用慢的分析案…

通过docker安装部署deepseek以及python实现

前提条件 Docker 安装:确保你的系统已经安装并正确配置了 Docker。可以通过运行 docker --version 来验证 Docker 是否安装成功。 网络环境:保证设备有稳定的网络连接,以便拉取 Docker 镜像和模型文件。 步骤一:拉取 Ollama Docker 镜像 Ollama 可以帮助我们更方便地管理…

快速傅里叶离散变换FFT (更新中)

声明:参考了 y y c yyc yyc 的 blog 和 PPT (from smwc) ,以及 w z r wzr wzr 的 blog 。 目录 Part 1 多项式Part 2 FFT概论Part 3 点值与插值Part 4 复数,单位根Part 5 Part 1 多项式 定义:对于有限数列 A 0 A_{0} A0​~ n…

w193基于Spring Boot的秒杀系统设计与实现

🙊作者简介:多年一线开发工作经验,原创团队,分享技术代码帮助学生学习,独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取,记得注明来意哦~🌹赠送计算机毕业设计600个选题excel文…

Spark--如何理解RDD

1、概念 rdd是对数据集的逻辑表示,本身并不存储数据,只是封装了计算逻辑,并构建执行计划,通过保存血缘关系来记录rdd的执行过程和历史(当一个rdd需要重算时,系统会根据血缘关系追溯到最初的数据源&#xff…

旋钮屏设备物联网方案,ESP32-C3无线通信应用,助力设备智能化升级

在智能家居的浪潮中,旋钮屏以其独特的交互方式和便捷的操作体验,逐渐成为智能家电控制面板上的新宠儿。从智能冰箱、洗衣机到烤箱、空气炸锅等设备,旋钮屏的应用无处不在。 通过简单的旋转和按压操作,用户可以轻松调节温度、时间…

crewai框架第三方API使用官方RAG工具(pdf,csv,json)

最近在研究调用官方的工具,但官方文档的说明是在是太少了,后来在一个视频里看到了如何配置,记录一下 以PDF RAG Search工具举例,官方文档对于自定义模型的说明如下: 默认情况下,该工具使用 OpenAI 进行嵌…

嵌入式工程师必学(143):模拟信号链基础

概述: 我们每天使用的许多电子设备,以及我们赖以生存的电子设备,如果不使用电子工程师设计的实际输入信号,就无法运行。 模拟信号链由四个主要元件组成:传感器、放大器、滤波器和模数转换器 (ADC)。这些传感器用于检测、调节模拟信号并将其转换为适合由微控制器或其他数…

C++11详解(二) -- 引用折叠和完美转发

文章目录 2. 右值引用和移动语义2.6 类型分类(实践中没什么用)2.7 引用折叠2.8 完美转发2.9 引用折叠和完美转发的实例 2. 右值引用和移动语义 2.6 类型分类(实践中没什么用) C11以后,进一步对类型进行了划分&#x…

NeetCode刷题第21天(2025.2.4)

文章目录 114 Gas Station 加油站115 Hand of Straights 顺子之手116 Merge Triplets to Form Target 将 Triplelet 合并到 Form Target117 Partition Labels 分区标签118 Valid Parenthesis String 有效的括号字符串119 Insert Interval 插入间隔120 Merge Intervals 合并区间…