19

我正在尝试用 procrun 包装弹簧靴“uber JAR”。

按预期运行以下工作:

java -jar my.jar

我需要我的 spring boot jar 在 Windows 启动时自动启动。最好的解决方案是将 jar 作为服务运行(与独立的 tomcat 相同)。

当我尝试运行它时,我得到“Commons Daemon procrun failed with exit value: 3”

查看 spring-boot 源代码,它看起来好像使用了自定义类加载器:

https://github.com/spring-projects/spring-boot/blob/master/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/JarLauncher.java

尝试直接运行我的主要方法时,我也会收到“ClassNotFoundException”。

java -cp my.jar my.MainClass

有没有一种方法可以用来在 Spring Boot jar 中运行我的 main 方法(而不是通过 JarLauncher)?

有没有人成功地将 spring-boot 与 procrun 集成?

我知道http://wrapper.tanukisoftware.com/。但是由于他们的许可证,我不能使用它。

更新

我现在已经设法使用 procrun 启动服务。

set SERVICE_NAME=MyService
set BASE_DIR=C:\MyService\Path
set PR_INSTALL=%BASE_DIR%prunsrv.exe

REM Service log configuration
set PR_LOGPREFIX=%SERVICE_NAME%
set PR_LOGPATH=%BASE_DIR%
set PR_STDOUTPUT=%BASE_DIR%stdout.txt
set PR_STDERROR=%BASE_DIR%stderr.txt
set PR_LOGLEVEL=Error

REM Path to java installation
set PR_JVM=auto
set PR_CLASSPATH=%BASE_DIR%%SERVICE_NAME%.jar

REM Startup configuration
set PR_STARTUP=auto
set PR_STARTIMAGE=c:\Program Files\Java\jre7\bin\java.exe 
set PR_STARTMODE=exe
set PR_STARTPARAMS=-jar#%PR_CLASSPATH%

REM Shutdown configuration
set PR_STOPMODE=java
set PR_STOPCLASS=TODO
set PR_STOPMETHOD=stop

REM JVM configuration
set PR_JVMMS=64
set PR_JVMMX=256

REM Install service
%PR_INSTALL% //IS//%SERVICE_NAME%

我现在只需要锻炼如何停止服务。我正在考虑对 spring-boot actuator 关闭 JMX Bean 做一些事情。

当我现在停止服务时会发生什么;windows无法停止服务(但将其标记为已停止),服务仍在运行(我可以浏览到本地主机),任务管理器中没有提到该进程(不是很好!除非我是盲人)。

4

5 回答 5

7

我遇到了类似的问题,但发现其他人(Francesco Zanutto)很亲切地写了一篇关于他们努力的博客文章。 他们的解决方案对我有用。我不相信他们为实现此代码所花费的时间。

http://zazos79.blogspot.com/2015/02/spring-boot-12-run-as-windows-service.html

与我在您的示例中看到的 exe 模式相比,他使用的是 jvm 启动和停止模式。有了这个,他能够扩展 Spring Boot 的 JarLauncher 来处理来自 Windows 服务的“启动”和“停止”命令,我相信你正在寻找优雅的关闭。

与他的示例一样,您将添加多个主要方法,具体取决于您的实现,您需要指出启动器现在应该调用哪些方法。我正在使用 Gradle,只需将以下内容添加到我的 build.gradle 中:

springBoot{
    mainClass = 'mydomain.app.MyApplication'
}

我的 Procrun 安装脚本:

D:\app\prunsrv.exe //IS//MyServiceName ^
--DisplayName="MyServiceDisplayName" ^
--Description="A Java app" ^
--Startup=auto ^
--Install=%CD%\prunsrv.exe ^
--Jvm=%JAVA_HOME%\jre\bin\server\jvm.dll ^
--Classpath=%CD%\SpringBootApp-1.1.0-SNAPSHOT.jar; ^
--StartMode=jvm ^
--StartClass=mydomain.app.Bootstrap ^
--StartMethod=start ^
--StartParams=start ^
--StopMode=jvm ^
--StopClass=mydomain.app.Bootstrap ^
--StopMethod=stop ^
--StopParams=stop ^
--StdOutput=auto ^
--StdError=auto ^
--LogPath=%CD% ^
--LogLevel=Debug

JarLauncher 扩展类:

package mydomain.app;


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 {
        Runnable runner = createMainMethodRunner(mainClass, args, classLoader);
        Thread runnerThread = new Thread(runner);
        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);
        }
    }

}

我的主要 Spring 应用程序类:

package mydomain.app;

import java.lang.management.ManagementFactory;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.ExitCodeGenerator;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan
@EnableAutoConfiguration
public class MyApplication {

    private static final Logger logger = LoggerFactory.getLogger(MyApplication.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(MyApplication.class);
            applicationContext = app.run(args);
            if (logger.isDebugEnabled()) {
                logger.debug("PID:" + ManagementFactory.getRuntimeMXBean().getName() + " Application started context:" + applicationContext);
            }
        }
    }
}
于 2015-04-27T18:44:35.107 回答
7

自从 Spring Boot 1.3 使用winsw以来,这现在是可能的。

文档将您引导至一个参考实现,该实现展示了如何设置服务。

于 2016-03-14T11:09:26.103 回答
4

从 springboot v1.2.2 开始,没有干净的方法可以使用 procrun 关闭 Spring Boot 应用程序,打包为 uber jar。请务必关注这些问题,因为这也是其他人也在问的问题:

目前尚不清楚springboot维护者是否/如何处理它。同时,考虑解压 uber jar 并忽略 Spring Boot 的 JarLauncher。

我对这个问题的原始回答(在历史上可以看到)提出了一种应该有效的方法(我认为有效),但不是由于 JarLauncher 中类加载器的处理方式。

于 2015-03-17T15:57:16.843 回答
3

刚刚遇到这个并想分享,我不久前解决了这个问题并发出了拉取请求。 https://github.com/spring-projects/spring-boot/pull/2520

您可以使用我的分叉版本,直到它被合并以使用 procrun 启动/停止。

于 2015-08-18T11:33:50.340 回答
2

远离winsw,它是用 .NET 制作的,我在我的客户环境中遇到了很多关于过时 Windows 的问题。

我推荐NSSM,它是使用纯 C 编写的,我在所有过时的 Windows 上都使用了它,没有任何问题。它具有相同的功能和更多...

这是一个batch script (.bat)如何使用它的示例:

rem Register the service
nssm install my-java-service "C:\Program Files\Java\jre1.8.0_152\bin\java.exe" "-jar" "snapshot.jar"
rem Set the service working dir
nssm set my-java-service AppDirectory "c:\path\to\jar-diretory"
rem Redirect sysout to file
nssm set my-java-service AppStdout "c:\path\to\jar-diretory\my-java-service.out"
rem Redirect syserr to file
nssm set my-java-service AppStderr "c:\path\to\jar-diretory\my-java-service.err"
rem Enable redirection files rotation
nssm set my-java-service AppRotateFiles 1
rem Rotate files while service is running
nssm set my-java-service AppRotateOnline 1
rem Rotate files when they reach 10MB
nssm set my-java-service AppRotateBytes 10485760
rem Stop service when my-java-service exits/stop
nssm set my-java-service AppExit Default Exit
rem Restart service when my-java-service exits with code 2 (self-update)
nssm set my-java-service AppExit 2 Restart
rem Set the display name for the service
nssm set my-java-service DisplayName "My JAVA Service"
rem Set the description for the service
nssm set my-java-service Description "Your Corp"
rem Remove old rotated files (older than 30 days)
nssm set my-java-service AppEvents Rotate/Pre "cmd /c forfiles /p \"c:\path\to\jar-diretory\" /s /m \"my-java-service-*.*\" /d -30 /c \"cmd /c del /q /f @path\""
rem Make a copy of my-java-service.jar to snapshot.jar to leave the original JAR unlocked (for self-update purposes)
nssm set my-java-service AppEvents Start/Pre "cmd /c copy /y \"c:\path\to\jar-diretory\my-java-service.jar\" \"c:\path\to\jar-diretory\snapshot.jar\""
于 2018-02-05T19:29:52.677 回答