您的位置:首页 > 编程语言 > Java开发

SpringBoot项目在window系统下以系统服务的方式部署jar包

2018-01-22 17:57 489 查看
1.部署背景

 作者的Java框架是以Spring cloud体系为基础构建的。基于Spring Boot一般有两种打包方式,一种是War包,一种是Jar包,抛开War包部署不讲,以jar包的形式部署是基于以下的考虑:

 A:SpringBoot本身的优势之一是内置tomcat,如果我们以war包的形式打包并部署在tomcat下,那么这个优势还有没有必要?
 B:官方建议使用velocity模板而不是jsp,是因为jar包形式运行对jsp支持不足,如果以war包部署,最初又何必纠结用jsp还是velecity

 这些只是作者的一点想法,写这篇文章的根本原因是,我们公司的服务器采用的是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包放置于tomca
4000
t 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后台服务化的功能上,代码是有变动的,但这些变动并不影响项目本身以其他形式部署或运行。变动主要有以下几点。

A:实现window服务需要实现服务的标准,需要实现启动和停止这两个基本的接口。
B:SpringBoot打包和普通的jar包打包是不同的,class文件会包装在BOOT-INF下,导致正常的打包,你会class not found。so,需要使用ant辅助,只需要将主方法提取出来即可,这并不影响jar包的完整性。如此改造即可,也就是说,你需要额外建多一个类,提供下项目启动的方法,还有项目停止的方法,以及将该类可读,可以正常访问。

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包支持

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-loader</artifactId>
</dependency>


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

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.8</version>
<executions>
<execution>
<phase>package</phase>
<configuration>
 <target>
 <zip destfile="${project.build.directory}/${project.build.finalName}.jar" update="true" compress="store">
  <fileset dir="${project.build.directory}/classes" includes="com/ycsys/business/Bootstrap.class" />
 </zip>
 </target>
</configuration>
<goals>
 <goal>run</goal>
</goals>
</execution>
</executions>
</plugin>


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.所有的配置及代码都已完成

94d5
 首先我们运行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:

 第一次写博客,其中诸多不便请原谅,有问题可以在博客上留言,看到会回复。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: