1.部署背景:
作者的Java框架是以Spring cloud体系为基础构建的。基于Spring Boot一般有两种打包方式,一种是War包,一种是Jar包,抛开War包部署不讲,以jar包的形式部署是基于以下的考虑:
这些只是作者的一点想法,写这篇文章的根本原因是,我们公司的服务器采用的是windows操作系统,并且是window server 2012版本,这跟最后决定采不采用docker有关。
Docker for window对windows系统有要求,server版本最低要求是2016,低版本的windows系统要上docker只能使用docker toolbox,是linux容器。
这样的话,把服务器装成linux系统不就可以了吗?何况,上docker解决的并非是根本的部署问题。
题外话不说,这里主要解决的问题是,在windows操作系统下,如何以系统服务的方式运行jar包,令其后台运行,并且宕机时,开机自启。
2.采用技术:
通常情况下,我们在windows操作系统上部署java web程序,是以tomcat为主要web应用服务器,将项目war包放置于tomcat webapps目录下,并在bin目录下安装service.bat脚本注册window服务,运行tomcatXw.exe进行管理,这种针对的是web项目。
SpringBoot内置tomcat,可以打包成jar包,通过主方法运行,本质上是独立的Java application,这种情况再放置于tomcat下运行,显然已不合适,当然,重新配置代码,并打成war包后仍可适用,这里我们只讲打成jar包的情况。在jar包的情况下,为此我查阅了网上资料。
在官网文档里,是有winsw的解决方案,链接如下:https://docs.spring.io/spring-boot/docs/1.5.9.RELEASE/reference/htmlsingle/#deployment-windows 点击打开链接,除此之外,还有一种解决方案,那就是与tomcat使用相同的技术:procrun,官方地址为:http://commons.apache.org/proper/commons-daemon/procrun.html。点击打开链接。我们这里采用的是procrun的方式。
3.个人说明:
网上有一些资料,但算不上许多,虽然我也是通过这些资料最终才实践成功的,但还是要写这篇文章,主要是因为那些文章的描述并不是很全,导致我在实践的过程中走了许多弯路,在这里,我可以负责任的说,如果你跟我有同样的需求,那么按照我的方案走,是可以解决你的问题的。
4.项目改造:
在实现window后台服务化的功能上,代码是有变动的,但这些变动并不影响项目本身以其他形式部署或运行。变动主要有以下几点。
5.核心插件:
procrun,下载地址为:http://www.apache.org/dist/commons/daemon/binaries/windows/commons-daemon-1.1.0-bin-windows.zip。点击打开链接,我们通过该插件,将jar包注册成window服务,并和tomcat完全一致的方式将其管理起来,不同的只是配置。
6.Jar包支持:
pom.xml文件里新增loader包支持
org.springframework.boot
spring-boot-loader
7.新增一个启动类
名字自取,我这里叫Bootstrap,放哪都可以,后面配置procrun会用到。
import org.springframework.boot.loader.JarLauncher;
import org.springframework.boot.loader.jar.JarFile;
public class Bootstrap extends JarLauncher {
private static ClassLoader classLoader = null;
private static Bootstrap bootstrap = null;
protected void launch(String[] args, String mainClass, ClassLoader classLoader, boolean wait)
throws Exception {
Thread.currentThread().setContextClassLoader(classLoader);
Thread runnerThread = new Thread(() -> {
try {
createMainMethodRunner(mainClass, args, classLoader).run();
}
catch(Exception ex) {}
});
runnerThread.setContextClassLoader(classLoader);
runnerThread.setName(Thread.currentThread().getName());
runnerThread.start();
if (wait == true) {
runnerThread.join();
}
}
public static void start (String []args) {
bootstrap = new Bootstrap ();
try {
JarFile.registerUrlProtocolHandler();
classLoader = bootstrap.createClassLoader(bootstrap.getClassPathArchives());
bootstrap.launch(args, bootstrap.getMainClass(), classLoader, true);
}
catch (Exception ex) {
ex.printStackTrace();
System.exit(1);
}
}
public static void stop (String []args) {
try {
if (bootstrap != null) {
bootstrap.launch(args, bootstrap.getMainClass(), classLoader, true);
bootstrap = null;
classLoader = null;
}
}
catch (Exception ex) {
ex.printStackTrace();
System.exit(1);
}
}
public static void main(String[] args) {
String mode = args != null && args.length > 0 ? args[0] : null;
if ("start".equals(mode)) {
Bootstrap.start(args);
}
else if ("stop".equals(mode)) {
Bootstrap.stop(args);
}
}
}
8.修改SpringBoot的application类
即SpringBoot的入口类。我的为ZooBusinessServiceApplication,修改main函数,启动时,是调用的springApplication.run,关闭时调用的是springApplication.exit,判断参数为stop字符串,这是重点。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.ExitCodeGenerator;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.ApplicationContext;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import java.lang.management.ManagementFactory;
@EnableDiscoveryClient
@SpringBootApplication
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableJpaAuditing
public class ZooBusinessServiceApplication {
private static final Logger logger = LoggerFactory.getLogger(ZooBusinessServiceApplication.class);
private static ApplicationContext applicationContext = null;
public static void main(String[] args) {
String mode = args != null && args.length > 0 ? args[0] : null;
if (logger.isDebugEnabled()) {
logger.debug("PID:" + ManagementFactory.getRuntimeMXBean().getName() + " Application mode:" + mode + " context:" + applicationContext);
}
if (applicationContext != null && mode != null && "stop".equals(mode)) {
System.exit(SpringApplication.exit(applicationContext, new ExitCodeGenerator() {
@Override
public int getExitCode() {
return 0;
}
}));
}
else {
SpringApplication app = new SpringApplication(ZooBusinessServiceApplication.class);
applicationContext = app.run(args);
if (logger.isDebugEnabled()) {
logger.debug("PID:" + ManagementFactory.getRuntimeMXBean().getName() + " Application started context:" + applicationContext);
}
}
}
}
9.配置打包方式
因为SpringBoot默认的打包方式比正常jar包多了BOOT-INF目录,因此使用ant将此类按照正常jar包的方式开放出来。在pom.xml里的build文件里配置,我们的主类为Bootstrap
org.apache.maven.plugins
maven-antrun-plugin
1.8
package
run
10.配置procrun
java程序上做的修改已经完毕。接下来就是配置procrun的步骤。首先我们创建一个目录,名字叫procrun,并在此目录下创建一个source目录,把下载的procrun全部丢进去。再另起一个procrun下另起一个目录,我的项目叫zoo-business-service,所以这个目录也叫zoo-business-service。另外再建一个目录,专门存放jar包,例如at-deploys-jars,加个at前缀主要是跟微信起名一样a开头可以保证显示在最前面,如下图,其他的是我其他项目,这是我自己的组织方式,不喜可按自己的方式来。
11.组织目录
将source目录下的prunmgr.exe拷贝至项目目录下,改名为服务名.exe,我的为zoo-business-service.exe,如下图所示,这里的目录install.bat主要是服务安装批处理执行文件,start.bat是启动服务批处理执行文件,uninstall.bat是卸载服务批处理执行文件,zoo-business-service.exe是与tomcat的管理程序一样的,打开后如下图
12.安装服务脚本
书写install.bat,如下,默认主类为MAIN_CLASS,所跑的jar包为CLASSPATH,JAVA_HOME配置jdk,SRV配置procrun的程序地址,LOGPATH配置日志输出地址,里面的所有配置项在控制程序.exe中都有对应操作配置,都可自行修改。详细配置可自行百度procrun,有各种教程。
@echo off
rem 设置程序名称
set SERVICE_EN_NAME=zoo-business-service
set SERVICE_CH_NAME=xxxxx平台服务
rem 设置java路径
set JAVA_HOME=%JAVA_HOME%
rem 设置程序依赖及程序入口类
cd..
set BASEDIR=%CD%
set CLASSPATH=%BASEDIR%\at-deploy-jars\zoo-business-service.jar
set MAIN_CLASS=com.ycsys.business.Bootstrap
rem 设置prunsrv路径
set SRV=%BASEDIR%\source\amd64\prunsrv.exe
rem 设置日志路径及日志文件前缀
set LOGPATH=%BASEDIR%\zoo-business-service\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 then install service
goto end
:foundJvm
echo 正在安装服务...
rem 安装
"%SRV%" //IS//%SERVICE_EN_NAME% --DisplayName="%SERVICE_CH_NAME%" "--Classpath=%CLASSPATH%" "--Install=%SRV%" "--JavaHome=%JAVA_HOME%" "--Jvm=%JVM%" --JvmMs=256 --JvmMx=1024 --Startup=auto --JvmOptions=-Djcifs.smb.client.dfs.disabled=false ++JvmOptions=-Djcifs.resolveOrder=DNS --StartMode=jvm --StartClass=%MAIN_CLASS% --StartMethod=start --StopMode=jvm --StopClass=%MAIN_CLASS% --StopMethod=stop --StopParams=stop --LogPath=%LOGPATH% --StdOutput=auto --StdError=auto
echo 安装服务完成。
pause
13.启动脚本
书写start.bat
@echo off
cd..
set BASEDIR=%CD%
set SERVICE_NAME=zoo-business-server
set MONITOR_PATH=%BASEDIR%\zoo-business-server\zoo-business-server.exe
echo start %SERVICE_NAME%
%MONITOR_PATH% //MR//%SERVICE_NAME%
echo 服务启动完成。
pause
14.卸载服务脚本
uninstall.bat
@echo off
cd..
set basedir=%CD%
set SERVICE_NAME=zoo-business-service
set SRV=%BASEDIR%\source\amd64\prunsrv.exe
echo 正在卸载服务...
"%SRV%" //DS//%SERVICE_NAME%
echo 服务卸载完毕。
pause
15.注意
注意,关闭配置参数,--StopParams=stop,这个参数必须要配,不然springboot程序无法正常关闭
16.所有的配置及代码都已完成。
首先我们运行install.bat,然后等待服务安装完成。完成成功后,即可用服务.exe程序操作项目的启动和关闭以及自启动了。最终运行效果如图。
17.最后说明
以上所有代码及步骤在spring boot 1.5.9 release 版本下测试通过,批文件编码最好和本机编码一致,不然可能出现乱码。
另附上我的helpme.txt.
该项目为spring boot部署为jar包形式的情况下提供在window系统部署为服务的支持,即支持jar包在window系统下部署为服务。
1.at-deploy-jars文件夹为jar包所存放的目录。
2.source文件夹为apache commons daemon procrun应用程序,与tomcat在window下的执行方式相同,核心文件,不可删。
3.其余目录为各自项目分支的文件。
4.目录下由install.bat,start.bat,uninstall.bat及监听控制程序.exe组成
5.install.bat文件为安装服务批处理文件
6.start.bat文件为启动服务批处理文件
7.uninstall.bat文件为卸载服务批处理文件。
8.操作方式:把项目安装为服务,安装完毕后打开项目控制程序.exe(项目名.exe)。start或者stop程序,注意,完整启动后方可stop,不然需任务管理器强制关闭。
9.jdk默认使用JAVA_HOME系统变量
10.日志文件在项目文件下logs文件
11.各种属性都可在控制程序中修改,如jdk可在Java.JavaVirtualMachine中修改。修改规律与tomcat配置完全一致(同一核心程序)。
12.启动与关闭实现在Bootstrap方法,stop方法需加stop字符串参数。
13.安装的服务默认开机自启。
14.更多属性配置请自行修改install.bat
18:总结:
这种部署方式不仅仅针对SpringBoot项目,只要是Jar包,都可以这样子部署,只要实现start方法和stop方法,并告知procrun即可,适用于所有jar包注册为window服务。
ps:
第一次写博客,其中诸多不便请原谅,有问题可以在博客上留言,看到会回复。