还在打jar包?maven-assembly了解一下?

2,049 阅读3分钟

背景

随着Springboot的流行,越来越多的项目打的都是jar包,而取代了war包。虽然jar包启动已经很简单了,但是如果我们需要修改配置文件某项配置,又需要修改,重新打成jar包,或者直接使用linux命令,修改jar包文件,但是这些都不方便。通过maven assembly插件,可以轻松将项目打成tar.gz包,便于我们修改配置

打包后项目结构

image.png

项目结构如图:

  1. tmp目录我们可以存放临时文件,可以编写定时任务定时清除
  2. run目录存放jar包运行的服务进程号
  3. logs目录存放运行时日志文件
  4. lib目录存放jar包
  5. bin目录存放可执行文件
  6. conf目录存放配置文件

步骤

pom文件添加插件

<build>
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <filtering>true</filtering>
            <excludes>
                <exclude>static/**</exclude>
                <exclude>bin/ci.sh</exclude>
            </excludes>
            <includes>
                <include>mybatis/**</include>
                <include>bin/*.sh</include>
                <include>*.properties</include>
                <include>*.yml</include>
                <include>*.yaml</include>
                <include>*.xml</include>
                <include>*.json</include>
            </includes>
        </resource>
        <!-- fonts file cannot use filter as the data structure of byte file will
            be changed via filter -->
        <resource>
            <directory>src/main/resources</directory>
            <filtering>false</filtering>
            <includes>
                <include>static/**</include>
                <include>templates/**</include>
            </includes>
        </resource>
    </resources>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <configuration>
                <excludes>
                    <exclude>*.properties</exclude>
                </excludes>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <configuration>
                <skip>true</skip>
                <testFailureIgnore>true</testFailureIgnore>
            </configuration>
        </plugin>
        <!--source-jar的编译-->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-source-plugin</artifactId>
            <executions>
                <execution>
                    <id>attach-sources</id>
                    <phase>package</phase>
                    <goals>
                        <goal>jar</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
        <!--maven打包插件-->
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <executions>
                <execution>
                    <goals>
                        <goal>repackage</goal>
                        <goal>build-info</goal>
                    </goals>
                    <configuration>
                        <!--主启动文件-->
                        <mainClass>com.sun.accsmbly.Application</mainClass>
                        <classifier>with-dependencies</classifier>
                    </configuration>
                </execution>
            </executions>
        </plugin>
        <!--   这个插件是关键,并且需要放在最后,否则可能lib目录下没有jar包   -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-assembly-plugin</artifactId>
            <configuration>
                <descriptors>
                    <!--   这个是assembly 所在位置  -->
                    <descriptor>src/assembly/assembly.xml</descriptor>
                </descriptors>
            </configuration>
            <executions>
                <!-- 配置执行器 -->
                <execution>
                    <id>make-assembly</id>
                    <!-- 绑定到package生命周期阶段上 -->
                    <phase>package</phase>
                    <goals>
                        <!-- 只运行一次 -->
                        <goal>single</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

创建目录,编写assembly插件的配置文件

详细可参考官网:maven.apache.org/plugins/mav…

<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
    <!--打包的后缀-->
    <id>distribution</id>
    <formats>
        <!--支持 zip,tar,tar.gz,tar.bz2,jar,dir,war 等 -->
        <format>tar.gz</format>
        <format>dir</format>
    </formats>
    <moduleSets>
        <moduleSet>
            <sources>
                <excludes>
                    <exclude>src/main/resources/*.properties</exclude>
                    <exclude>src/main/resources/*.xml</exclude>
                </excludes>
            </sources>
        </moduleSet>
    </moduleSets>
    <fileSets>
        <fileSet>
            <outputDirectory>/sql</outputDirectory>
            <directory>sql</directory>
            <includes>
                <include>*.sql</include>
            </includes>
        </fileSet>
        <fileSet>
            <outputDirectory>/tmp</outputDirectory>
            <directory>target</directory>
            <excludes>
                <exclude>**/*</exclude>
            </excludes>
        </fileSet>
        <fileSet>
            <outputDirectory>/logs</outputDirectory>
            <directory>target</directory>
            <excludes>
                <exclude>**/*</exclude>
            </excludes>
        </fileSet>
        <fileSet>
            <outputDirectory>/run</outputDirectory>
            <directory>target</directory>
            <excludes>
                <exclude>**/*</exclude>
            </excludes>
        </fileSet>
        <fileSet>
            <outputDirectory>/lib</outputDirectory>
            <directory>target</directory>
            <includes>
                <include>*-with-dependencies.jar</include>
            </includes>
        </fileSet>
        <fileSet>
            <outputDirectory>/bin</outputDirectory>
            <directory>target/classes/bin</directory>
            <lineEnding>unix</lineEnding>
            <fileMode>0744</fileMode>
        </fileSet>
        <fileSet>
            <outputDirectory>conf</outputDirectory>
            <directory>target/classes</directory>
            <includes>
                <include>*.yaml</include>
                <include>*.yml</include>
                <include>*.properties</include>
                <include>*.xml</include>
            </includes>
        </fileSet>
    </fileSets>
</assembly>

在resources/bin目录下编写相关的启动脚本,方便我们启动和停止配置服务

image.png

以下是我通常使用的脚本,可供参考:

  • ctl.sh
#!/bin/sh

# resolve links - $0 may be a softlink
PRG="$0"
while [ -h "$PRG" ] ; do
  ls=`ls -ld "$PRG"`
  link=`expr "$ls" : '.*-> (.*)$'`
  if expr "$link" : '/.*' > /dev/null; then
    PRG="$link"
  else
    PRG=`dirname "$PRG"`/"$link"
  fi
done
PRGDIR=`dirname "$PRG"`

# Only set PRG_HOME if not already set
[ -z "$PRG_HOME" ] && PRG_HOME=`cd "$PRGDIR/.." ; pwd`

#echo "PRG_HOME: $PRG_HOME"

if [ -r "$PRG_HOME"/bin/setenv.sh ]; then
  . "$PRG_HOME"/bin/setenv.sh
fi

# Some parameters initializaion
[ -z "$FILE_ENCODING" ] && FILE_ENCODING=UTF-8
[ -z "$TMP_DIR" ] && TMP_DIR="$PRG_HOME"/tmp
[ -z "$DATA_DIR" ] && DATA_DIR="$PRG_HOME"/data
[ -z "$RUN_DIR" ] && RUN_DIR="$PRG_HOME"/run
[ -z "$LOG_DIR" ] && LOG_DIR="$PRG_HOME"/logs
[ -z "$CONF_DIR" ] && CONF_DIR="$PRG_HOME"/conf

if [ ! -d "$TMP_DIR" ]; then
mkdir -p $TMP_DIR
fi

PRG_NAME=@project.artifactId@
PRG_VERSION=@project.version@
JAR_FILE=$PRG_HOME/lib/$PRG_NAME-$PRG_VERSION-with-dependencies.jar

PRG_PIDFILE=$RUN_DIR/$PRG_NAME.pid
LIB_PATH="$PRG_HOME/lib"
CONF_PATH=$CONF_DIR

if [ "x$STANDALONE" = "xtrue" ]; then
    SPRING_CONFIGS="file:$CONF_PATH/application.properties"
    SPRING_CONFIGS="$SPRING_CONFIGS,file:$CONF_PATH/application-dev.properties"
    SPRING_CONFIGS="$SPRING_CONFIGS,file:$CONF_PATH/application-prod.properties"
    echo "CLUSTER MODE: standalone"
else
    SPRING_CONFIGS="$SPRING_CONFIGS,file:$CONF_PATH/bootstrap.properties"
 echo "CLUSTER MODE: cluster"
fi

JAVA_OPTS="$JAVA_OPTS -Djava.ext.dirs=$JAVA_HOME/jre/lib/ext:$LIB_PATH"
JAVA_OPTS="$JAVA_OPTS -Dfile.encoding=$FILE_ENCODING"
JAVA_OPTS="$JAVA_OPTS -Djava.tmp.dir=$TMP_DIR"
JAVA_OPTS="$JAVA_OPTS -Djava.io.tmpdir=$TMP_DIR"

JAVA_OPTS="$JAVA_OPTS -Dspring.profiles.active=$PROFILE"
JAVA_OPTS="$JAVA_OPTS -Dspring.config.location=$SPRING_CONFIGS"

PRG_START="java -server $JAVA_OPTS -jar $JAR_FILE"

PRG_STATUS=""
PID=""
ERROR=0

get_pid() {
    PID=""
    PIDFILE=$1
    # check for pidfile
    if [ -f "$PIDFILE" ] ; then
        PID=`cat $PIDFILE`
    fi
}

get_PRG_PID() {
    get_pid $PRG_PIDFILE
    if [ ! "$PID" ]; then
        return
    fi
    if [ "$PID" -gt 0 ]; then
        PRG_PID=$PID
    fi
}

is_service_running() {
    PID=$1
    if [ "x$PID" != "x" ] && kill -0 $PID 2>/dev/null ; then
        RUNNING=1
    else
        RUNNING=0
    fi
    return $RUNNING
}

is_running() {
    get_PRG_PID
    is_service_running $PRG_PID
    RUNNING=$?
    if [ $RUNNING -eq 0 ]; then
        PRG_STATUS="$PRG_NAME not running"
    else
        PRG_STATUS="$PRG_NAME already running"
    fi
    return $RUNNING
}

start() {
    is_running
    RUNNING=$?
    if [ $RUNNING -eq 1 ]; then
        echo "$0 $ARG: $PRG_NAME  (pid $PRG_PID) already running"
 exit
    fi
 echo $PRG_START
    $PRG_START &
    echo $! > $PRG_PIDFILE
    COUNTER=40
    while [ $RUNNING -eq 0 ] && [ $COUNTER -ne 0 ]; do
        COUNTER=`expr $COUNTER - 1`
        sleep 3
        is_running
        RUNNING=$?
    done
    if [ $RUNNING -eq 0 ]; then
        ERROR=1
    fi
    if [ $ERROR -eq 0 ]; then
     echo "$0 $ARG: $PRG_NAME  started at port $PORT"
     sleep 2
    else
     echo "$0 $ARG: $PRG_NAME  could not be started"
     ERROR=3
    fi
}

stop() {
    NO_EXIT_ON_ERROR=$1
    is_running
    RUNNING=$?
    if [ $RUNNING -eq 0 ]; then
        echo "$0 $ARG: $PRG_STATUS"
        if [ "x$NO_EXIT_ON_ERROR" != "xno_exit" ]; then
            exit
        else
            return
        fi
    fi
 
 kill $PRG_PID

    COUNTER=40
    while [ $RUNNING -eq 1 ] && [ $COUNTER -ne 0 ]; do
        COUNTER=`expr $COUNTER - 1`
        sleep 3
        is_running
        RUNNING=$?
    done

    is_running
    RUNNING=$?
    if [ $RUNNING -eq 0 ]; then
            echo "$0 $ARG: $PRG_NAME stopped"
            rm -f $PRG_PIDFILE
        else
            echo "$0 $ARG: $PRG_NAME could not be stopped"
            ERROR=4
    fi
}

help(){
    echo    "--------------------------------------------------"
    echo    "Usage: ctl.sh  help|start|stop|status|cleanpid|deamon|version"
    echo -e "\n"
    echo    "start      - start the process"
    echo    "stop       - stop the process"
    echo    "status     - query the process status"
    echo    "cleanpid   - delete the process pid file"
    echo    "cleanlogs  - delete all log files"
    echo    "cleantmp   - delete all temp files"
    echo    "clean      - delete all temp/log/pid files"
    echo    "deamon     - set the process as a deamon"
    echo    "version    - view build version"
    echo    "help       - print help"
}

cleanlogs() {
    rm -rf $LOG_DIR/*
}

cleantmp() {
    rm -rf $TMP_DIR/*
}

cleanpid() {
    rm -f $PRG_PIDFILE
}

clean() {
    cleanpid
    cleantmp
    cleanlogs
}

version() {
    jar -xf $JAR_FILE META-INF/build-info.properties
    cat META-INF/build-info.properties
    rm -rf META-INF
}

if [ "x$1" = "xstart" ]; then
    start
elif [ "x$1" = "xstop" ]; then
    stop
elif [ "x$1" = "xstatus" ]; then
    is_running
    echo "$PRG_STATUS"
elif [ "x$1" = "xcleanpid" ]; then
    cleanpid
elif [ "x$1" = "xcleantmp" ]; then
    cleantmp
elif [ "x$1" = "xcleanlogs" ]; then
    cleanlogs
elif [ "x$1" = "xclean" ]; then
    clean
elif [ "x$1" = "xversion" ]; then
    version
elif [ "x$1" = "xdeamon" ]; then
    is_running
    RUNNING=$?
    if [ $RUNNING -eq 1 ]; then
        echo "$0 $ARG: $PRG_NAME  (pid $PRG_PID) already running"
    else
        echo "$0 $ARG: $PRG_NAME  (pid $PRG_PID) not running"
        start
    fi
elif [ "x$1" = "xhelp" ]; then
    help
else
 help
fi

exit $ERROR
  • deamon.sh
#!/bin/sh
# execute commond below to auto startup after startup of computer:

#####################
# resolve links - $0 may be a softlink
#####################

PRG="$0"
while [ -h "$PRG" ] ; do
  ls=`ls -ld "$PRG"`
  link=`expr "$ls" : '.*-> (.*)$'`
  if expr "$link" : '/.*' > /dev/null; then
    PRG="$link"
  else
    PRG=`dirname "$PRG"`/"$link"
  fi
done
PRGDIR=`dirname "$PRG"`

#####################
# start
#####################

nohup sh "$PRGDIR"/ctl.sh deamon >/dev/null 2>&1 &
  • setenv.sh
# Local address default as below, if you have more net-interface, you must specify one
LOCAL_ADDRESS=`/sbin/ifconfig -a|grep inet|grep -v 127.0.0.1|grep -v inet6|awk '{print $2}'|tr -d "addr:"`
#echo LOCAL_ADDRESS:$LOCAL_ADDRESS

# Solves the problem of multiple NIC
LOCAL_ADDRESS=`echo $LOCAL_ADDRESS | awk '{print $1}'`
#echo LOCAL_ADDRESS RANDOM:$LOCAL_ADDRESS


JAVA_OPTS="$JAVA_OPTS -Xms8g"
JAVA_OPTS="$JAVA_OPTS -Xmx8g"
JAVA_OPTS="$JAVA_OPTS -XX:+HeapDumpOnOutOfMemoryError"
JAVA_OPTS="$JAVA_OPTS -Duser.timezone=GMT+08"
#JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote"
#JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.port=0"
#JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.ssl=false"
#JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.authenticate=false"

#JAVA_OPTS="$JAVA_OPTS -Dserver.address=$LOCAL_ADDRESS"

# which spring profile will be actived
# PROFILE="dev"

# standalone or cluster? true if standalone, otherwise false as cluster
STANDALONE="true"

#TMP_DIR=/tmp
#FILE_ENCODING=UTF-8
  • start.sh
#!/bin/sh

#####################
# resolve links - $0 may be a softlink
#####################

PRG="$0"
while [ -h "$PRG" ] ; do
  ls=`ls -ld "$PRG"`
  link=`expr "$ls" : '.*-> (.*)$'`
  if expr "$link" : '/.*' > /dev/null; then
    PRG="$link"
  else
    PRG=`dirname "$PRG"`/"$link"
  fi
done

PRGDIR=`dirname "$PRG"`
EXECUTABLE=ctl.sh
CONSOLE_OUT=$PRGDIR/../logs/console.out

if [ ! -x "$PRGDIR"/"$EXECUTABLE" ]; then
 echo "Cannot find $PRGDIR/$EXECUTABLE"
 echo "The file is absent or does not have execute permission"
 echo "This file is needed to run this program"
exit 1
fi

nohup "$PRGDIR"/"$EXECUTABLE" start > "$CONSOLE_OUT" 2>&1 &
echo "Started success!"
  • stop.sh
#!/bin/sh

#####################
# resolve links - $0 may be a softlink
#####################

PRG="$0"
while [ -h "$PRG" ] ; do
  ls=`ls -ld "$PRG"`
  link=`expr "$ls" : '.*-> (.*)$'`
  if expr "$link" : '/.*' > /dev/null; then
    PRG="$link"
  else
    PRG=`dirname "$PRG"`/"$link"
  fi
done

PRGDIR=`dirname "$PRG"`
EXECUTABLE=ctl.sh
CONSOLE_OUT=$PRGDIR/../logs/console.out

if [ ! -x "$PRGDIR"/"$EXECUTABLE" ]; then
 echo "Cannot find $PRGDIR/$EXECUTABLE"
 echo "The file is absent or does not have execute permission"
 echo "This file is needed to run this program"
exit 1
fi

exec "$PRGDIR"/"$EXECUTABLE" stop
echo "Stopped success!"

mavan编译

image.png

打包完项目如下

image.png

image.png

通过这种的项目结构,我们部署、运维就更加简单拉