Java工程打包tar.gz完整流程

Java工程打成tar包的博文很多, 但是很多都只是給了簡單的配置文件demo, 但是作為一項工程, 簡單的配置是不能成事的. 大部分開源軟件都是tar.gz格式, 很明顯非常的工程化, 規(guī)范化.
所以僅僅一些隨筆性質(zhì)的博文還是不能夠讓我掌握, 得以在生產(chǎn)環(huán)境中使用.

于是, 自己資料加線上測試探索, 基本掌握一點入門使用.

您將了解到:

  • 打tar包的基本配置
  • 啟動/停止腳本
  • shell啟動java程序classpath問題
  • shell結(jié)束java程序時, java自己在結(jié)束進程之前如何做些后續(xù)處理工作
  • 分環(huán)境打包和assembly插件基本使用

目錄結(jié)構(gòu)

|- javapro
    |- bin
    |- conf
    |- logs
    |- lib
    |- README.MD
    ... 
  • lib: 自己的工程打成的jar包以及依賴jar包
  • conf:配置文件
  • logs: 日志
  • bin: 腳本, 如啟動,停止腳本

打包配置

通常流行使用assembly插件打包, 包括tar包.
這一步麻煩的是配置, 所以需要整理好模板, 以備不時之需.

pom.xml
引入插件.

<plugins>
   <plugin>
            <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
             
                <version>2.2.1</version>
                <executions>
                    <execution>
                        <id>assemble</id>
                        <goals>
                            <goal>single</goal>
                        </goals>
                        <phase>package</phase>
                    </execution>
                </executions>
                <configuration>
                    <appendAssemblyId>false</appendAssemblyId>
                    <attach>false</attach>
                </configuration>
  </plugin>

</plugins>


profile標(biāo)簽配置

<profiles>
 <profile>
            <id>release</id>
            <activation>
                <activeByDefault>true</activeByDefault>

                <property>
                    <name>env</name>
                    <value>release</value>
                </property>
            </activation>

            <build>
                <plugins>
                    <plugin>
                        <artifactId>maven-assembly-plugin</artifactId>
                        <configuration>
                            <!-- 發(fā)布模式使用的maven assembly插件描述文件 -->
                            <descriptors>
                                <descriptor>${basedir}/src/main/assembly/release.xml</descriptor>
                            </descriptors>
                            <!-- 如果一個應(yīng)用的包含多個deploy模塊,如果使用同樣的包名, 如果把它們復(fù)制的一個目錄中可能會失敗,所以包名加了 artifactId以示區(qū)分 -->
                            <finalName>${project.artifactId}-${project.version}</finalName>
                            <!-- scm 要求 release 模式打出的包放到頂級目錄下的target子目錄中 -->
                            <outputDirectory>${project.parent.build.directory}</outputDirectory>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
        </profile>

</profiles>


上文的 profile可以配置多環(huán)境(開發(fā),測試,生成..., 這里僅用一個環(huán)境release演示).
可以看到release環(huán)境中引入了./src/main/assembly/release.xml文件. 這個文件就是決定了打包的目錄結(jié)構(gòu)和文件.

release.xml

<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
    <id>dist</id>
    <formats>
        <format>tar.gz</format>
    </formats>
    <includeBaseDirectory>false</includeBaseDirectory>
    <fileSets>
        <fileSet>
            <directory>.</directory>
            <outputDirectory>/</outputDirectory>
            <includes>
                <include>README*</include>
                <include>test.csv</include>
            </includes>
        </fileSet>
        <fileSet>
            <directory>./src/main/bin</directory>
            <outputDirectory>bin</outputDirectory>
            <includes>
                <include>**/*</include>
            </includes>
            <fileMode>0755</fileMode>
        </fileSet>
        <fileSet>
            <directory>./src/main/conf</directory>
            <outputDirectory>/conf</outputDirectory>
            <includes>
                <include>**/*</include>
            </includes>
            <fileMode>0644</fileMode>
        </fileSet>
        <fileSet>
            <directory>./src/main/meta</directory>
            <outputDirectory>/meta</outputDirectory>
            <includes>
                <include>**/*</include>
            </includes>
            <fileMode>0644</fileMode>
        </fileSet>
        <fileSet>
            <directory>./src/main/resources</directory>
            <outputDirectory>/</outputDirectory>
            <includes>
                <include>**/*</include>
            </includes>
        </fileSet>
        <fileSet>
            <directory>target</directory>
            <outputDirectory>logs</outputDirectory>
            <excludes>
                <exclude>**/*</exclude>
            </excludes>
        </fileSet>
    </fileSets>
    <dependencySets>
        <dependencySet>
            <outputDirectory>lib</outputDirectory>
            <excludes>
                <exclude>junit:junit</exclude>
            </excludes>
        </dependencySet>
    </dependencySets>
</assembly>

可單獨了解相關(guān)專題: [java分環(huán)境打包部署][assembly插件打包]

到此, 最簡單的基本配置完成了, 可以達(dá)成tar.gz包了. 但是問題是, 部署的時候, 那什么來啟動java程序. 請看下文.

啟動/關(guān)閉腳本

我的shell腳本處于幼兒園水平, 以下腳本都是復(fù)制粘貼修改的.
啟動腳本: startup.sh

#!/bin/bash



case "`uname`" in
    Linux)
        bin_abs_path=$(readlink -f $(dirname $0))
        ;;
    *)
        bin_abs_path=`cd $(dirname $0); pwd`
        ;;
esac

echo "腳本位置: $bin_abs_path"
base=${bin_abs_path}/..
#base=$(dirname $(cd `dirname $0`;pwd))
echo "base path: $base"
echo "cd to $base"
cd $base


export LANG=en_US.UTF-8
export BASE=$base

echo "cd to: $base"

#can't run repeatedly
if [ -f $base/bin/addr.pid ] ; then
    echo "found bin/addr.pid , Please run stop.sh first ,then startup.sh" 2>&2
    exit 1
fi

## set java path
if [ -z "$JAVA" ] ; then
  JAVA=$(which java)
fi



str=`file $JAVA_HOME/bin/java | grep 64-bit`
if [ -n "$str" ]; then
    JAVA_OPTS="-server -Xms1024m -Xmx1536m -Xmn256m -XX:SurvivorRatio=2 -XX:PermSize=96m -XX:MaxPermSize=256m -Xss256k -XX:-UseAdaptiveSizePolicy -XX:MaxTenuringThreshold=15 -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:+HeapDumpOnOutOfMemoryError"
else
    JAVA_OPTS="-server -Xms1024m -Xmx1024m -XX:NewSize=256m -XX:MaxNewSize=256m -XX:MaxPermSize=128m "
fi

JAVA_OPTS=" $JAVA_OPTS -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true -Dfile.encoding=UTF-8"

for i in $base/lib/*;
    do CLASSPATH=$i:"$CLASSPATH";
done


#$JAVA $JAVA_OPTS -classpath .:$CLASSPATH com.jfai.addr.StartUp 1>>$base/bin/nohup.out 2>&1 &
$JAVA $JAVA_OPTS -classpath .:$CLASSPATH com.jfai.addr.StartUp 1>$base/bin/nohup.out 2>&1 &

echo $! > $base/bin/addr.pid
echo "Process addr is running..., pid=$!"

停止腳本: stop.sh

#!/bin/bash

cygwin=false;
case "`uname`" in
    CYGWIN*)
        cygwin=true
        ;;
esac

get_pid() {
    STR=$1
    PID=$2
    if $cygwin; then
        JAVA_CMD="$JAVA_HOME\bin\java"
        JAVA_CMD=`cygpath --path --unix $JAVA_CMD`
        JAVA_PID=`ps |grep $JAVA_CMD |awk '{print $1}'`
    else
        if [ ! -z "$PID" ]; then
            JAVA_PID=`ps -C java -f --width 1000|grep "$STR"|grep "$PID"|grep -v grep|awk '{print $2}'`
        else
            JAVA_PID=`ps -C java -f --width 1000|grep "$STR"|grep -v grep|awk '{print $2}'`
        fi
    fi
    echo $JAVA_PID;
}


base=`dirname $0`/..
pidfile=$base/bin/addr.pid
if [ ! -f "$pidfile" ];then
    echo "addr.pid is not found, addr is not running? exit"
    exit
fi

pid=`cat $pidfile`
if [ "$pid" == "" ] ; then
    pid=`get_pid "addr"`
fi

echo -e "`hostname`: stopping addr, pid=$pid ... "
kill $pid

LOOPS=0
while (true);
do
    gpid=`get_pid "addr" "$pid"`
    if [ "$gpid" == "" ] ; then
        echo "Oook! cost:$LOOPS"
        `rm $pidfile`
        break;
    fi
    let LOOPS=LOOPS+1
    sleep 1
done

附錄

classpath問題

一開始, 筆者在用startup.sh啟動時, 碰到:

  • 若在~/bin/目錄下執(zhí)行sh startup.sh時, classpath就是: ~/bin/, 這回導(dǎo)致: new File("conf/xx.properties")(相對路徑)找不到文件的, 因為它會將相對路徑轉(zhuǎn)成: ~/bin/conf/xx.properties. 這顯然不是我想要的.
  • 若在~/路徑下執(zhí)行: sh bin/startup.sh, 能夠成功轉(zhuǎn)成: ~/conf/xx.properties
    ('~'代表省略的父路徑)

根據(jù)問題詳情頁的解答, 解決了這個問題.

關(guān)鍵是要在腳本的一開始就cd 到項目根路徑

停止腳本殺死進程時, 如何讓java進程在退出之前做些事情?

Runtime.getRuntime().addShutdownHook(...)可以搞定.

請看演示代碼:

    public static void main(String[] args) {

        //Test:
        System.out.println("啟動 ...");

        Runtime.getRuntime().addShutdownHook(new Thread(){
            @Override
            public void run() {
                log.info("執(zhí)行shutdown...");
                //這里演示shutdown線程可以讀取到主線程改變的變量
                System.out.println("主線程循環(huán)了"+count+"次");
                running = false;
            }
        });

        while (running) {
            System.out.println("Not shutdown, running ...");
            count++;
        }
        //kill pid后, 主線程會停止
        System.out.println("退出循環(huán)體, 程序?qū)⑼顺?);

        //end test

效果

注意: stop.sh中kill命令不能加-9參數(shù).
加上-9之后, 是不會執(zhí)行到添加到shutdownhook中的線程任務(wù)的.

錯誤寫法

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容