需求: 由于spring boot 结合cloud 可以有多个微服务,可以打很多jar包,其中有很多maven库是重复的,package生成的jar中由于重复会占用大量的体积。造成在操作系统上部署时占用磁盘空间,安装过程也较慢。 同时,为了便于在操作系统上一键化部署jar服务,可以编写install.exe或install.sh来实现。在云平台上由于有install文件,也可以方便以组件的形式安装部署和卸载。
目前我了解的将Java程序部署为Windows服务的方式有java service wrapper和Apache Commons Daemon两种。
1.概述
1.1 为什么使用Apache Commons Daemon
java service wrapper使用简单,集成方法简单,不修改任何代码,一般情况下已满足需求。但是,java service wrapper只对java程序的开启及关闭进行操作,若需要对程序启动前及关闭前进行一些自定义的操作(如启动时初始化工作,关闭时释放某些资源或进行特殊操作),此时就可以使用apache commons daemon了。
1.2 Apache Commons Daemon介绍
Apache common deamon是用来提供java服务的安装,实现将一个普通的 Java 应用变成系统的一个后台服务,在linux下部署为后台运行程序,在windows部署为windows服务(著名的tomcat就是使用它实现启动及停止的。提供启动、停止、卸载等操作)。详细介绍可见commons-daemon官网(http://commons.apache.org/proper/commons-daemon/index.html)。相对java service wrapper,commons daemon需要自己写少许代码,即按规定要求编写程序启动及关闭的入口类,仅此而已。
2.下载安装
下载commons-daemon主程序commons-daemon-1.1.0-bin.zip,解压出commons-daemon-1.1.0.jar
下载procrun,官网的procrun页面只对它的使用进行讲解,在哪里下载即没有提及,这里特别提醒一下,需要在这里(http://www.apache.org/dist/commons/daemon/binaries/windows/)下载procrun,下载commons-daemon-1.1.0-bin-windows.zip。解压出文件如下:
将你的Java程序打成jar包,commons-daemon-1.1.0.jar和prunsrv.exe,prunmgr.exe,install.bat,uninstall.bat放到同一个文件夹下(注意64位的机器要用amd64文件夹下的prunsrv.exe)
3.spring boot 程序启动类实现
pom.xml文件里新增loader包支持
org.springframework.boot
spring-boot-loader
commons-daemon
commons-daemon
1.1.0
在***Aplication.class 相同目录下,新增一个启动类。
package com.mboot.wheelbase;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.loader.PropertiesLauncher;
/**
* Daemon jsvc方式注册部署jar服务时的启动类
*/
public class DaemonMainLauncher extends PropertiesLauncher {
private static Logger LOGGER = LoggerFactory.getLogger(DaemonMainLauncher.class);
private static DaemonMainLauncher daemonMainLauncher = null;
public static void init(String[] args) {
}
public static void start(String[] args) {
if (daemonMainLauncher == null)
daemonMainLauncher = new DaemonMainLauncher();
try
{
args = daemonMainLauncher.getArgs(args);
daemonMainLauncher.launch(args);
} catch (Exception ex) {
System.exit(1);
}
}
public static void destroy() {
}
public static void start() {
String[] args = { "start" };
start(args);
}
public static void stop(String[] args) {
try {
System.exit(0);
} catch (Exception ex) {
System.exit(1);
}
}
public static void stop() {
String[] args = { "stop" };
stop(args);
}
public static void main(String[] args)
{
try {
String mode = (args != null) && (args.length > 0) ? args[0] : null;
if ("start".equals(mode)) {
start(args);
}
else if ("stop".equals(mode)) {
stop(args);
}
else
start(args);
}
catch (Exception e) {
System.exit(1);
}
}
}
pom.xml文件里配置bulid 插件。
org.springframework.boot
spring-boot-maven-plugin
true
ZIP
com.mboot
wheel-base
org.apache.maven.plugins
maven-jar-plugin
true
lib/
com.mboot.wheelbase.WheelBaseApplication
org.apache.maven.plugins
maven-dependency-plugin
copy-dependencies
prepare-package
copy-dependencies
${project.build.directory}/lib
false
false
true
编译打包生成wheel-base-0.0.1.jar 包,这个包即为瘦身后的包,大概只有几十KB,非常小。它不包含maven中引入的软件依赖库,而生成的依赖库则在pom.xml中配置的lib路径里面。将包含依赖库的lib文件复制出来作为接下来的公共依赖库使用。
下面来写服务安装脚本 install.bat
@echo off
rem 设置程序名称
set SERVICE_EN_NAME=WheelBase
set SERVICE_CH_NAME=WheelBase工作流引擎
set DESCRIPTION=wheel-base工作组件
rem 设置java路径
set _JAVA_HOME=%JAVA_HOME%
rem 设置程序依赖及程序入口类
set BASEDIR=%CD%
set BPM_LIBS=%BASEDIR%\server\lib
set BPM_MAIN_LIBS=%BASEDIR%\server\main_lib
set BPM_EXT_LIBS=%BASEDIR%\server\ext_lib
set BPM_LOG=%BASEDIR%\config\log
set _main=com.mboot.wheelbase.WheelBaseApplication
set _LauncherClass=com.mboot.wheelbase.DaemonMainLauncher
set CLASSPATH=%BPM_LIBS%\*;%BPM_MAIN_LIBS%\*;%BPM_EXT_LIBS%\*;%BPM_LOG%;%_JAVA_HOME%\lib\dt.jar;%_JAVA_HOME%\lib\tools.jar;%BPM_MAIN_LIBS%\wheel-base-0.0.1.jar;
rem 设置prunsrv路径
set SRV=%CD%\prunsrv.exe
rem 设置日志路径及日志文件前缀
set LOGPATH=%BASEDIR%\tmp\logs
rem 输出信息
echo SERVICE_NAME: %SERVICE_EN_NAME%
echo JAVA_HOME: %JAVA_HOME%
echo MAIN_CLASS: %MAIN_CLASS%
echo prunsrv path: %SRV%
rem 设置jvm
if "%JVM%" == "" goto findJvm
if exist "%JVM%" goto foundJvm
:findJvm
set "JVM=%_JAVA_HOME%\jre\bin\server\jvm.dll"
if exist "%JVM%" goto foundJvm
echo can not find jvm.dll automatically,
echo please use COMMAND to localation it
echo for example : set "JVM=%_JAVA_HOME%\jre\bin\server\jvm.dll"
echo then install service
goto end
:foundJvm
rem 安装
"%SRV%" //IS//"%SERVICE_EN_NAME%" --DisplayName="%SERVICE_EN_NAME%" --Classpath="%CLASSPATH%" --Description="%DESCRIPTION%" --Install="%SRV%" --JavaHome="%_JAVA_HOME%" --Jvm="%JVM%" --JvmMs=256 --JvmMx=1024 --Startup=auto --JvmOptions=-Djcifs.smb.client.dfs.disabled=false ++JvmOptions=-Djava.io.tmpdir="%CD%"/tmp ++JvmOptions=-Dloader.main=%_main% ++JvmOptions=-DBPM_HOME=%BASEDIR% ++JvmOptions=-Djcifs.resolveOrder=DNS --StartPath="%BASEDIR%" --StartMode=jvm --StartClass=%_LauncherClass% --StartMethod=start --StopPath="%BASEDIR%" --StopMode=jvm --StopClass=%_LauncherClass% --StopMethod=stop --LogPath="%LOGPATH%" --StdOutput=auto --StdError=auto
:end
要注意的是classpath中的路径应有你Java程序的所有依赖包以及你自己打包程序的那个jar包,上述解压出的commons-daemon-1.1.0.jar也应该在这个路径中。
%SRV% 为prunsrv.exe所在路径
%LOGPATH% 为日志路径
--StartMethod 为程序入口开始方法(对应部署服务之后的启动) 必须要在自己打包的Java程序中写这个方法,根据自己的业务逻辑编写即可。(在某些博客中看到这个方法必须命名为start,实践证明并不需要,但你一定要有这个开始方法)。
--StopMethod 为程序入口结束方法(对应部署服务后的停止) 必须要在自己打包的Java程序中写这个方法,根据自己的业务逻辑编写即可。(在某些博客中看到这个方法必须命名为start,实践证明并不需要,但你一定要有这个结束方法)。
要格外注意的是以上两个方法一定要声明为public static void,并且一定要带参数(如string[] args),不然Apache Commons Daemon会报错,详情请见Apache Commons Daemon的官方说明文档。建议你所打包的Java入口程序中不写main方法,只写开始和结束方法。
再来写服务卸载脚本 uninstall.bat
@echo off
set BASEDIR=%CD%
set SERVICE_NAME=WheelBase
set "SRV=%BASEDIR%\prunsrv.exe"
%SRV% //DS//%SERVICE_NAME%
:end
简言之,到这一步为止,你的目录结构下应该有你自己打的jar包,commons-daemon-1.1.0.jar,prunsrv.exe,prunmgr.exe,install.bat,uninstall.bat
其目录结构如下,其中conf,server,tmp的子目录按照install.bat 文件中配置的文件路径创建即可:
此时运行install.bat脚本即可,运行之后会发现操作系统的服务中多了一个WheelBase服务,可以正常的启动和停止。tmp目录下中的log目录会记录启动日志,如下:
该日志记录了服务注册的成功与失败,以及停止卸载等。
在操作系统的服务中,点击“启动”后,spring boot 启动成功,表示服务注册部署成功。
如果要卸载该服务,运行uninstal.exe即可。
以上就是jar包瘦身与服务部署注册的完整过程,其中不涉及到业务代码的修改。只需要嵌入启动类即可。