app-server.sh
这里写目录标题
- app-server.sh
- 1.准备Java运行环境
- 第一部分
- 代码解析
- 代码逻辑
- 示例运行
- 注意事项
- 总结
- 第二部分
- 1. 检查`JAVA_HOME`是否已设置
- 2. macOS环境下的Java路径查找
- 方法1:使用`/usr/libexec/java_home`
- 方法2:使用系统框架路径
- 3. 非macOS环境下的Java路径查找
- 方法:通过`javac`查找
- 4. 如果`JAVA_HOME`仍然未设置
- 5. 设置`JAVA`变量
- 总结
- 2.Service Configuration
- 脚本解析
- 1. 确定 `BASE_DIR`
- 2. 切换到 `BASE_DIR`
- 3. 设置相关路径
- 4. 创建日志目录
- 脚本的作用
- 示例运行
- 注意事项
- 3.JVM Configuration
- 代码解析
- 1. JVM 内存和线程设置
- 2. 垃圾回收和性能优化
- 3. 垃圾回收调优
- 4. 垃圾回收日志
- 5. 国际化和编码
- 6. 引导类路径
- 7. 链路追踪
- 8. 构建 Java 命令
- 注意事项
- 1. 垃圾回收器的选择
- 2. 日志路径
- 3. 链路追踪
- 4. 调试
- 示例输出
- 4.命令行
- 1. 启动应用(`start`)
- 2. 前台运行应用(`run`)
- 3. 停止应用(`stop`)
- 4. 重启应用(`restart`)
- 改进建议
- 1. 日志输出
- 2. 优雅停止
- 3. 检查应用是否真正停止
- 4. 重启逻辑优化
- 优化后的脚本
#!/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安装路径。以下是代码的逐步解析和解释:
代码解析
**[ ! -e "$JAVA_HOME/bin/java" ]**
这是一个条件判断语句,检查$JAVA_HOME/bin/java
文件是否存在。-e
是一个测试操作符,用于检查文件是否存在。**&&**
这是一个逻辑“与”操作符,表示只有当前面的条件为真(即$JAVA_HOME/bin/java
不存在)时,才会执行后面的命令。**JAVA_HOME=$HOME/jdk/java**
如果$JAVA_HOME/bin/java
不存在,则将JAVA_HOME
设置为$HOME/jdk/java
。$HOME
是一个环境变量,表示当前用户的主目录。- 后续的
**[ ! -e "$JAVA_HOME/bin/java" ]**
和**JAVA_HOME**
赋值语句
这些语句依次尝试不同的路径,直到找到一个有效的java
可执行文件路径。如果某个路径有效,则JAVA_HOME
会被设置为该路径。 **unset JAVA_HOME**
如果所有尝试的路径都不包含java
可执行文件,则最终会执行unset JAVA_HOME
,将JAVA_HOME
环境变量清除。
代码逻辑
这段代码的逻辑是:
- 检查当前
$JAVA_HOME/bin/java
是否存在。 - 如果不存在,则依次尝试以下路径:
$HOME/jdk/java
/usr
/usr/java
/opt/taobao/java
- 如果以上路径都无效,则清除
JAVA_HOME
变量。
示例运行
假设初始时JAVA_HOME
未设置,或者$JAVA_HOME/bin/java
不存在,脚本会依次尝试以下路径:
$HOME/jdk/java/bin/java
如果存在,则JAVA_HOME
被设置为$HOME/jdk/java
。- 如果不存在,尝试
/usr/bin/java
。如果存在,则JAVA_HOME
被设置为/usr
。 - 如果仍然不存在,尝试
/usr/java/bin/java
。如果存在,则JAVA_HOME
被设置为/usr/java
。 - 如果仍然不存在,尝试
/opt/taobao/java/bin/java
。如果存在,则JAVA_HOME
被设置为/opt/taobao/java
。 - 如果所有路径都无效,最终执行
unset JAVA_HOME
,清除JAVA_HOME
变量。
注意事项
- 路径顺序
路径的顺序很重要。脚本会依次尝试每个路径,直到找到有效的java
可执行文件。如果某个路径有效,后续的路径将被忽略。 - 环境变量的持久性
这段脚本通常用于脚本文件中(如bash
脚本或profile
文件)。如果在命令行中直接运行,JAVA_HOME
的设置只对当前终端会话有效。如果希望永久生效,需要将这段代码添加到~/.bashrc
、~/.bash_profile
或/etc/profile
等文件中。 - 多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命令。
总结
这段脚本的主要功能是:
- 检查
JAVA_HOME
是否已设置。 - 如果未设置,则根据操作系统类型(macOS或非macOS)尝试自动查找Java的安装路径。
- 如果找到有效的路径,则设置
JAVA_HOME
。 - 如果未找到有效的路径,则提示用户手动设置
JAVA_HOME
。 - 最终设置
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}
:创建目录及其所有父目录(如果不存在)。
脚本的作用
这段脚本的主要作用是:
- 确定应用程序的根目录(
BASE_DIR
)。 - 设置应用程序的 JAR 文件路径、配置文件路径、日志文件路径、库文件路径等。
- 确保日志目录存在(如果不存在则创建)。
示例运行
假设脚本文件名为 start.sh
,并且脚本所在的目录为 /path/to/app
,运行脚本时可以这样操作:
/path/to/app/start.sh arg1 arg2
- 如果提供了第二个参数
arg2
,则BASE_DIR
会被设置为arg2
。 - 如果没有提供第二个参数,则
BASE_DIR
会被设置为脚本所在的目录/path/to/app
。
注意事项
- 路径分隔符:
- 在 Linux 和 macOS 系统中,路径分隔符是
/
。 - 如果你需要在 Windows 系统中运行类似的脚本,需要特别注意路径分隔符(Windows 使用
\
)。
- 在 Linux 和 macOS 系统中,路径分隔符是
- 权限问题:
- 确保脚本有执行权限,可以通过
chmod +x start.sh
命令设置。 - 确保脚本所在的目录和相关路径有适当的读写权限。
- 确保脚本有执行权限,可以通过
- 调试:
- 如果脚本运行时出现问题,可以通过在脚本中添加更多的
echo
命令来打印变量值,帮助定位问题。
- 如果脚本运行时出现问题,可以通过在脚本中添加更多的
3.JVM Configuration
从你的代码来看,你已经正确地构建了 JAVA_OPTS
和 JAVA_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 -ef
和grep
查找所有与${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 -2
和kill -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}
是多余的,可以移除。 - 重启逻辑可以复用
stop
和start
的逻辑,避免重复代码。
优化后的脚本
以下是优化后的脚本:
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