摸索了一晚上外加多年经验终于有了这个最佳实践.
环境
- assembly
- springboot
- idea
- java8
- logback
需求
- jar包要可执行:
java -jar jar_name
. - 依赖分离: 主业务代码独立成可执行jar, 其他所有依赖放到
./lib
下. - 配置文件分离:
application.yml
,logback.xml
等配置文件不在jar内, 而是在项目根目录下. 这样方便中途查看和修改配置. - 分环境打包: 我这里分了三个环境
dev
,test
,prod
. - 脚本启动,停止,重启应用.
配置参考
pom.xml
dev
dev
true
test
test
prod
prod
maven-resources-plugin
utf-8
true
${project.artifactId}-${project.version}
src/main/resources
true
org.apache.maven.plugins
maven-compiler-plugin
1.8
UTF-8
org.apache.maven.plugins
maven-assembly-plugin
src/main/resources/assembly.xml
make-my-jar-with-dependencies
package
single
${project.artifactId}
org.apache.maven.plugins
maven-jar-plugin
*.xml
*.yml
*.properties
*.sh
public
conf
com.gx.app.GxAppApplication
true
lib/
true
false
./
assembly.xml
assembly
dir
${project.basedir}
/
true
application.yml
application-${profileActive}.yml
*.xml
${project.build.directory}/site
docs
${project.basedir}/src/main/resources
/
true
0775
**/*
${project.basedir}/src/main/resources/conf/${profileActive}
/
true
0755
*.properties
/lib
${artifact.artifactId}-${artifact.baseVersion}${dashClassifier?}.${artifact.extension}
true
runtime
${project.groupId}:${project.artifactId}:*
provided
/
${project.groupId}:${project.artifactId}:*
logback.xml
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
${LOG_HOME}/${project.artifactId}.log
${LOG_HOME}/rolling/${project.artifactId}_%d{yyyy-MM-dd}.%i.log.zip
30
100MB
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
${LOG_HOME}/sql/${project.artifactId}_sql_%d{yyyy-MM-dd}.log
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
${LOG_HOME}/dao/${project.artifactId}_dao_%d{yyyy-MM-dd}.log
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
${LOG_HOME}/error/${project.artifactId}_error.log
${LOG_HOME}/error/${project.artifactId}_error_%d{yyyy-MM-dd}.log
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
ERROR
ACCEPT
DENY
application.yml
spring:
application:
name: GxApp
profiles:
active: @profileActive@
logging:
level:
root: info
com.gx: debug
启动脚本
#!/bin/bash
#此脚本为Linux下启动java程序的通用脚本。(包含启动,停止,重启)
#cd 进入脚本执行的bin目录,sh run.sh start(启动) | stop(停止)| restart(重启)
#
#bin目前路径以及相关目录路径
cd `dirname $0`
BIN_DIR=`pwd`
DEPLOY_DIR=`pwd`
#LOG_BACK=-Dlogback.configurationFile=$DEPLOY_DIR/logback.xml
JAVA_DEBUG_OPTS=" -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n "
JAVA_JMX_OPTS=" -Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Djava.rmi.server.hostname=$ip "
#拼接全路径jar包名, 为了精确匹配jar包, 能确保准确定位pid
JAR_NAME=$BIN_DIR/`ls ${project.artifactId}*.jar`
cmd=$2
echo "$cmd"
start(){
echo "DEPLOY_DIR=$DEPLOY_DIR"
PIDS=`ps --no-heading -C java -f --width 1000 | grep "$DEPLOY_DIR" |awk '{print $2}'`
if [ -n "$PIDS" ]; then
echo "ERROR: The $SERVER_NAME already started!"
echo "PID: $PIDS"
exit 1
fi
echo -e "Starting the $JAR_NAME ...\c"
nohup java -Xms256M -Xmx1024M -XX:PermSize=128M -XX:+HeapDumpOnOutOfMemoryError -verbose:gc -XX:+PrintGCDetails -Xloggc:./gc.log -XX:+PrintGCDateStamps -jar $JAR_NAME > /dev/null 2>&1 &
COUNT=0
while [ $COUNT -lt 1 ]; do
echo -e ".\c"
sleep 1
COUNT=`ps --no-heading -C java -f --width 1000 | grep "$JAR_NAME" | awk '{print $2}' | wc -l`
if [ $COUNT -gt 0 ]; then
break
fi
done
echo "start OK!"
PIDS=`ps --no-heading -C java -f --width 1000 | grep "$JAR_NAME" | awk '{print $2}'`
echo "PID: $PIDS"
echo $PIDS > .pid
}
stop(){
PIDS=`ps --no-heading -C java -f --width 1000 | grep "$JAR_NAME" |awk '{print $2}'`
if [ -z "$PIDS" ]; then
echo "ERROR: The $SERVER_NAME does not started!"
fi
echo -e "Stopping the $SERVER_NAME ...\c"
for PID in $PIDS ; do
kill $PID > /dev/null 2>&1
done
COUNT=0
while [ $COUNT -lt 1 ]; do
echo -e ".\c"
sleep 1
COUNT=1
for PID in $PIDS ; do
PID_EXIST=`ps --no-heading -p $PID`
if [ -n "$PID_EXIST" ]; then
COUNT=0
break
fi
done
done
echo "stop OK!"
echo "PID: $PIDS"
rm .pid
}
case $1 in
start)
start;
;;
stop)
stop;
;;
restart)
echo "############ Application of '"$JAR_NAME"' restarting....############"
stop;
sleep 2
start;
;;
*)
echo "Usage: startup.sh {start|stop|restart}"
;;
esac
exit 0
目录结构
打包产物
配置文件, 静态资源都在根下.
主程序执行jar包结构
没有配置文件了.
坑点
如何配置不当:
- 打包时配置文件能够隔离. 但IDE里本地运行时, 找不到配置文件, 因为配置文件被排除了.
- spring 的devtools 一定要设置成
scope provided
, 让它不参与打包. 这样可以避免在设置当前目录加入了classpath, 而日志文件又写入当亲目录内时, 应用不停的重启. (这是因为检测到了classpath内内容变化就会热部署
, 就会重启). - 找不到
logback.xml
配置文件. 解决: M1: 加入当前目录到classpath; M2: 启动命令中指定文件.