美文网首页java编程
Java工程打包tar.gz完整流程

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

作者: Nisus_Liu | 来源:发表于2018-08-07 18:12 被阅读0次

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中的线程任务的.

错误写法

相关文章

网友评论

    本文标题:Java工程打包tar.gz完整流程

    本文链接:https://www.haomeiwen.com/subject/ddmavftx.html