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

Java工程打成tar包的博文很多, 但是很多都只是给了简单的配置文件demo, 但是作为一项工程, 简单的配置是不能成事的. 大部分开源软件都是tar.gz格式, 很明显非常的工程化, 规范化.
所以仅仅一些随笔性质的博文还是不能够让我掌握, 得以在生产环境中使用.

于是, 自己资料加线上测试探索, 基本掌握一点入门使用.

您将了解到:

  • 打tar包的基本配置
  • 启动/停止脚本
  • shell启动java程序classpath问题
  • shell结束java程序时, java自己在结束进程之前如何做些后续处理工作
  • 分环境打包和assembly插件基本使用

目录结构

|- 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标签配置

<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>
                            <!-- 发布模式使用的maven assembly插件描述文件 -->
                            <descriptors>
                                <descriptor>${basedir}/src/main/assembly/release.xml</descriptor>
                            </descriptors>
                            <!-- 如果一个应用的包含多个deploy模块,如果使用同样的包名, 如果把它们复制的一个目录中可能会失败,所以包名加了 artifactId以示区分 -->
                            <finalName>${project.artifactId}-${project.version}</finalName>
                            <!-- scm 要求 release 模式打出的包放到顶级目录下的target子目录中 -->
                            <outputDirectory>${project.parent.build.directory}</outputDirectory>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
        </profile>

</profiles>


上文的 profile可以配置多环境(开发,测试,生成..., 这里仅用一个环境release演示).
可以看到release环境中引入了./src/main/assembly/release.xml文件. 这个文件就是决定了打包的目录结构和文件.

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>

可单独了解相关专题: [java分环境打包部署][assembly插件打包]

到此, 最简单的基本配置完成了, 可以达成tar.gz包了. 但是问题是, 部署的时候, 那什么来启动java程序. 请看下文.

启动/关闭脚本

我的shell脚本处于幼儿园水平, 以下脚本都是复制粘贴修改的.
启动脚本: 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/目录下执行sh startup.sh时, classpath就是: ~/bin/, 这回导致: new File("conf/xx.properties")(相对路径)找不到文件的, 因为它会将相对路径转成: ~/bin/conf/xx.properties. 这显然不是我想要的.
  • 若在~/路径下执行: sh bin/startup.sh, 能够成功转成: ~/conf/xx.properties
    ('~'代表省略的父路径)

根据问题详情页的解答, 解决了这个问题.

关键是要在脚本的一开始就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("执行shutdown...");
                //这里演示shutdown线程可以读取到主线程改变的变量
                System.out.println("主线程循环了"+count+"次");
                running = false;
            }
        });

        while (running) {
            System.out.println("Not shutdown, running ...");
            count++;
        }
        //kill pid后, 主线程会停止
        System.out.println("退出循环体, 程序将退出");

        //end test

效果

注意: stop.sh中kill命令不能加-9参数.
加上-9之后, 是不会执行到添加到shutdownhook中的线程任务的.

错误写法

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容