Springboot 2.0打包与自定义launch.script

阅读更多

    随着我们对springboot 2.0的了解逐步加深,以及部署环境(linux以及容器化)对打包方式的要求变化,我们逐步希望将springboot应用打包成“可执行jar”并在启动时更便捷的指定系统参数。

 

    1、将springboot 打包成可执行jar,然后通过类似于如下方式启动或者关闭程序:

$> ./application.jar start
$> ./application.jar stop

$> JAVA_OPTS=-Xmx2g ./application.jar start

 

    2、除此之外,我们可能希望统一管理springboot打包的方式,比如限定日志目录、统一指定JVM参数,或者在启动时额外的从配置中心pull一些静态文件等,这些特殊要求,原生的launch.script无法完成,我们需要扩展launch.scipt或者自定义它,但是达成这个结果,还是有些困难,因为原生的机制无法支持。

 

 

    面临的问题:

    1、即使我们重新开发了launch.script,借助,但是这个脚本只能放在project的本地目录,如果我们将此脚本嵌入在外部的jar中(主要是不希望所有的项目都重复这个脚本)则可能无法加载。

    2、即使我们使用,但是这个行内脚本无法解决复杂的执行。

 

    解决问题的方式:

    1、我们为了保留springboot原生的launch.script的绝大部分功能,所以从springboot源码中copy一份。

    2、开发一个maven-plugin,将我们自定义的launch.script和自定义的inlined-conf.script文件都放在此插件模块中。我们的初心是希望此插件可以被众多项目通用,script统一管理(修改、升级),业务项目只需要引用即可。

    3、此maven-plugin,功能非常简单,就是在package阶段,将这两个script复制到项目的target目录中。

    4、spring-boot-maven-plugn的配置稍微调整一下,就可以引用到这两个script了,因为这个两个script已经通过我们自研的plugin复制到了项目target目录。

 

一、maven-plugin开发

     目的很简单,就是引用此插件的web项目,在打包时,将两个script复制到web项目的target目录中,以供spring-boot-maven-plugin使用,spring-boot-maven-plugin将使用launch.script作为前脸,来启动项目。

 

    此插件的处于package阶段,主要包含:

    1、LauncherWriterMojo:在package期间,用于复制脚本文件到使用插件的web项目的target目录。

    2、inlined-conf.script:spring-boot-maven-plugin支持的配置,内部主要是指定一些springboot可执行jar支持的一些系统参数。

    3、launch.script:启动脚本,底板来自springboot自带的源码,我们在内部增加了一些功能,比如拼装JVM参数、系统参数配置等。

 

 

    LauncherWriterMojo.java

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;

import java.io.*;

/**
 * @author liuguanqing
 * created 2019/3/8 5:21 PM
 **/
@Mojo(name = "package", defaultPhase = LifecyclePhase.PREPARE_PACKAGE, requiresProject = true, threadSafe = true, requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME, requiresDependencyCollection = ResolutionScope.COMPILE_PLUS_RUNTIME)
public class LauncherWriterMojo extends AbstractMojo {

    @Parameter(defaultValue = "${basedir}/target", required = true)
    private File outputDirectory;

    public void setOutputDirectory(File outputDirectory) {
        this.outputDirectory = outputDirectory;
    }

    @Override
    public void execute() throws MojoExecutionException, MojoFailureException {
        try {
            copy("launch.script");
            getLog().info("launch.script has been created.");
            copy("inlined-conf.script");
            getLog().info("inlined-conf.script has been created.");
        } catch (IOException ie) {
            throw new MojoExecutionException("launch.script written error!",ie);
        }
    }

    private void copy(String filename) throws IOException{
        InputStream inputStream = getClass().getResourceAsStream("/" + filename);
        BufferedWriter writer = null;
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream,"UTF-8"));
            File target = new File(outputDirectory + "/" + filename);
            target.setExecutable(true,false);
            target.setWritable(true,false);
            target.setReadable(true,false);
            writer = new BufferedWriter(new FileWriter(target));
            while (true) {
                String line = reader.readLine();
                if (line == null) {
                    break;
                }
                writer.write(line);
                writer.newLine();
            }
            writer.flush();
        }  finally {
            if (inputStream != null) {
                inputStream.close();
            }
            if (writer != null) {
                writer.close();
            }
        }
    }
}

 

    inlined-conf.script

MODE=service; identity=run; PID_FOLDER=./var; LOG_FOLDER=./; LOG_FILENAME=std.out; pidFilename=pid; JAVA_OPTS="$JAVA_OPTS -XX:NewRatio=2 -XX:G1HeapRegionSize=8m -XX:MaxMetaspaceSize=256m -XX:MaxTenuringThreshold=10 -XX:+UseG1GC -XX:InitiatingHeapOccupancyPercent=45 -XX:MaxGCPauseMillis=200 -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintReferenceGC -XX:+PrintAdaptiveSizePolicy -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=6 -XX:GCLogFileSize=32m -Xloggc:./var/run/gc.log.$(date +%Y%m%d%H%M) -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./var/run/java_pid.hprof -Dfile.encoding=UTF-8 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=${JMX_PORT:-0} -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false"; mkdir -p var/run

 

    简单描述一下:此行内脚本,主要是降低用户配置spring-boot-maven-plugin的复杂度,将日志目录、PID文件、除了heap大小之外的其他通用JVM参数等,统一指定,这样使用此插件打包的项目就可以更加规范。

 

    launch.script:代码copy自spring-boot自带的,本文你可以认为没有什么差别。

 

 

二、使用方式

    1、你的web项目或者module的pom.xml


    com.??.commons
    meteor-spring-boot-maven-plugin
    ${meteor-project.version}
    
        
            
                package
            
        
    


    org.springframework.boot
    spring-boot-maven-plugin
    ${spring-boot.version}
    
        true
        
            ${basedir}/target/inlined-conf.script
        
        ${basedir}/target/launch.script
    
    
        
            
                repackage
            
        
    

 

    当然为了统一插件的使用,你可能会将上述配置放在一个parent-pom.xml中或者一个parent项目中,其他使用此框架的项目直接引用上述插件而不再指定插件中的配置即可。例如:

    
        application
        
            
                com.??.commons
                meteor-spring-boot-maven-plugin
            
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    

 

    2、使用maven打包之后,生成的application.jar就是可执行文件,且已经将我们自定义的launch.script作为前脸,执行时会指定我们的script。

你可能感兴趣的:(Springboot 2.0打包与自定义launch.script)