所以,你的项目已经投入生产,你想要使用操作系统来启动它,而不是每次手动启动。但是如何使用常规的jar文件来实现呢?
有很多选择:
I)将带有java –jar launch或exe文件的bat文件放在Windows启动中(从jar制作exe文件后)。
Windows 7 : C:\users\All Users\Start Menu\Programs\Startup(Admin) or User home directory(%userProfile%)Windows 10 : In Run shell:startup
这将解决问题,但你的应用程序最好只在任何用户的第一次登录时启动(如果你将bat文件放入所有用户的启动项中)。但是,正如你所知,一个好的管理员不会在服务器上操作。
II)在系统启动时开始制作Windows任务,启动bat文件。
这个选项完美地解决了启动问题,但是应用程序的操作到此为止,不能在任何时间进行启动和停止,也不能因错误而重新启动。
**III)**使用sc.exe(还有_Srvinstw.exe_——GUI版本,但我们是真正的程序员,必须使用命令行)将jar作为win服务运行。
这是一个小程序,来自资源工具包,可以从exe文件创建服务。
但我们有一个jar文件,对吧? 没问题!有尝试的选项:
1) 直接使用参数运行java.exe -jar。
2) 从jar制作exe文件并将其转换为服务。
3) 使用_srvany.exe_
a) 从中运行java.exe –jar
b) 编写带有启动java -jar的bat文件,并从 srvany 运行它。
IV) 使用第三方软件。
让我们忽略前两个最不有趣的选项。
0)应用程序
首先,我们将编写一个小应用程序,具有黑杰克和资源,根据传递的参数,以便在尝试将其作为服务工作时感受到最大的痛苦。
应用程序将:
-
咒骂没有参数
-
根据传递的参数读取特定的设置文件
-
通过外部库解析这些设置
-
每5秒钟将相同的设置倒入日志
应用程序示例可在此处找到:
https://github.com/lkSnatch/tutorials/tree/master/jar-as-os-service
我的结果将是可执行的jar文件,具有独立的库和资源,位于单独的文件夹中。你可以随心所欲地尝试。
1)使用sc.exe启动服务
好的,要阅读sc帮助,只需输入:
sc –bla_bla
结果将是“错误。未识别的命令”,以及有关可能命令的进一步帮助。
A)使用java.exe –jar创建服务
sc create TestServ4 binPath= “E:\Java\JavaPrograms\jdk-11.0.1\bin\java.exe -Xmx400m -jar E:\Java\JavaPrograms\test\withoutSpaces\mainTutorial-1.0-SNAPSHOT.jar amazing prod” type= own start= auto error= normal DisplayName= TestService4
我有一个jdk绕着应用程序转,不依赖于服务器中的版本,并且给我的程序一个大尺寸的稳定性。如果java.exe在环境变量中,则不需要完整路径。值得注意的是一个烦人而不显眼的特点:“binPath=
”——在任何参数之后(这里是binPath),立即出现相等符号,然后在它和参数值之间需要一个空格。
很好,服务已经创建好了,运行它!哎呀..:
(Windows无法在本地计算机上启动TestService6服务。错误1053:服务未能及时响应启动或控制请求。)
(我的TestService在示例中具有不同的数字,因为该服务并不总是立即被删除,有时只是标记为删除)
以防万一,让我们看一下应用程序日志是否出现了。但是它出现了!只有一个微小的日志,包含从尝试启动服务到发出错误的时刻的信息。发生了什么?就是这样:
-
服务开始使用所需的参数启动java.exe
-
我们的应用程序开始工作
-
Windows服务管理器等待java.exe对其成功(或不成功)启动的响应一段时间
-
但是java.exe没有返回任何内容,因为它不欠任何人
-
服务管理器倒计时以秒为单位并关闭java
结论:并不是每个exe文件都可以作为服务运行。不能简单地运行任何exe文件作为服务,它必须经过特别优化,以用作Windows服务(至少返回有关其启动的一些数据)。
或者尝试_javaw_?试试吧,但是名称中的_double-V_并不意味着双重胜利。
B)使用cmd /c java.exe -jar创建服务
如果你在cmd中运行cmd并在其中运行java,整个链路会关闭吗?让我们从小的方面开始——仅从cmd运行java,并在服务启动时指定cmd作为_binpath:
sc create TestServ3 binPath= “cmd /c E:\Java\JavaPrograms\jdk-11.0.1\bin\java.exe -Xmx400m -jar E:\Java\JavaPrograms\test\withoutSpaces\mainTutorial-1.0-SNAPSHOT.jar amazing prod” type= own start= auto error= normal DisplayName= TestService3
同样,在超时后它会掉下来。但是只有cmd进程结束了它毫无价值的生命,而我们的超级java应用程序继续呼吸和愉悦。
它工作了吗?是的!作为完整的服务吗?没有!太阳穴都磨平了!
但是将我们的jar文件放在带空格的文件夹中(“转义”)会更有趣:
sc create TestServ6 binPath= “cmd /c E:\Java\JavaPrograms\jdk-11.0.1\bin\java.exe -Xmx400m -jar \”E:\Java\JavaPrograms\test\with Spaces\mainTutorial-1.0-SNAPSHOT.jar\” amazing prod” type= own start= auto error= normal DisplayName= TestService6
看一下注册表:
(DeleteFlag是我设置的,所以请假设你在这里没有看到它)看起来还不错,只是参数应该用引号括起来才能完全满意。
结论:技巧很好,但并不总是百分之百有用。
C)使用srvany.exe java.exe –jar创建服务
srvany.exe也是来自资源工具包的程序工具。它的工作很简单——运行指定的应用程序。也就是说,我们将srvany.exe注册为一个服务,并指定参数指向我们的程序。顺序:启动服务→运行srvany→运行我们的应用程序。
sc create TestServ6 binPath= “E:\Java\JavaPrograms\test\with Spaces\srvany.exe” type= own start= auto error= normal DisplayName= TestService6
添加启动srvany的参数:
reg add “HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\TestServ6\Parameters” /v “Application” /d “\”E:\Java\JavaPrograms\jdk-11.0.1\bin\javaw.exe\” -Xmx400m -jar \”E:\Java\JavaPrograms\test\with Spaces\mainTutorial-1.0-SNAPSHOT.jar\” \”amazing\” \”prod\””
启动...观察...等待...观察...它运行了!就这样结束了吗?还是缺少什么?为什么要发明第三方软件(除了自我教育和广告目的)?你可以试着杀死进程或在应用程序中调用异常。我们将平稳地进入下一个点。
结论:srvany可行,但捕获错误很烦人。
2)使用第三方软件NSSM启动服务。
这里是一份不同软件的简短列表:
-
jsvc (变态版:“它可以通过Cygwin模拟层在Win32上运行”(с))
我们将使用最后一个。对于懒惰的读者,重复一下问题:为什么我们需要第三方软件?对于他们,来自nssm网站的答案是:
“srvany和其他服务助手程序很糟糕,因为它们无法处理运行为服务的应用程序的故障。如果你使用这样的程序,则可能会看到列出的服务已启动,而实际上应用程序已死亡。nssm监视正在运行的服务,如果它死亡,将重新启动它。”
它是如何工作的?一切都一样:nssm.exe以指定的参数安装为服务,当服务启动时,nssm.exe本身启动,它又启动java.exe –jar。这里是任务管理器中的清晰运行情况:
现在,开始操作。执行“nssm.exe install TestServ
”启动GUI。
(这里的参数是:-jar“E:\Java\JavaPrograms\test\with Spaces\mainTutorial-1.0-SNAPSHOT.jar”“amazing”“prod”)
我们可以使用命令行完成相同的操作:
nssm install TestServ “E:\Java\JavaPrograms\jdk-11.0.1\bin\java.exe” “-Xmx400m -jar \”E:\Java\JavaPrograms\test\with Spaces\mainTutorial-1.0-SNAPSHOT.jar\” \”amazing\” \”prod\””nssm set TestServ DisplayName “Test Service”nssm set TestServ AppDirectory “E:\Java\JavaPrograms\test\with Spaces”nssm set TestServ AppRestartDelay 1000nssm set TestServ Description “Start java.exe with params -Xmx400m -jar mainTutorial.jar amazing prod”
对我们来说,指定AppDirectory很重要,因为我们使用的配置和库就在那里,与我们的应用程序一起位于资源和res子文件夹中。
如果你已经厌倦了这个服务或者在创建它时出了差错,那么运行以下命令:
nssm remove TestServ
让我们一起回顾一下注册表项:
(是的,是的,我的“with Spaces”和“withoutSpaces”文件夹在命令和截图中都没有,就当你从未看见过这些,因为我太懒了,不想重做)
所有参数和引号都在正确的位置。看一下服务信息:
运行服务..万岁!一切正常,日志被写入!
jun. 23, 2019 9:33:29 AM net.snatchTech.tutorials.App mainCONFIG: The app has been started with name: amazing_prodjun. 23, 2019 9:33:30 AM net.snatchTech.tutorials.App lambda$main$3INFO:servicename : serviceid : prodname_id : service_prodoptionsnumber : 1string : smthjun. 23, 2019 9:33:35 AM net.snatchTech.tutorials.App lambda$main$3INFO:servicename : serviceid : prodname_id : service_prodoptionsnumber : 1string : smth
试着杀死java或从内部使你的应用程序崩溃。
结论:第三方软件也有生存的权利,不是由我们辉煌的双手、大脑和双腿编写的。
3)自动化!
通过处理程序启动30个服务器的服务是没有意义的,需要脚本!忽略机器的扩散,至少让我们创建一个服务,通过更改一个变量名称来正确运行当前服务器指定的配置。
但脚本很简单!确信?如果名称中添加了空格呢?痛苦就隐藏在微调引号及其转义的细节中。假设我们将_AppDir_放入一个变量中。我们应该如何在参数中写入它,以及作为当前目录应该如何写入?引号在哪里必要,在哪里不必要?
set AppDir=”E:\Java\JavaPrograms\test\with Spaces\” orset AppDir=”E:\Java\JavaPrograms\test\with Spaces\\” orset AppDir=”E:\Java\JavaPrograms\test\with Spaces”
在参数中使用:
nssm install %SrvName% %PathToProg% “%ProgParams% -jar %AppDir%%AppName% %AppParams% ornssm install %SrvName% \”%PathToProg%\” “%ProgParams% -jar \”%AppDir%%AppName%\” %AppParams%” ornssm install %SrvName% %PathToProg% “%ProgParams% -jar \”%AppDir%\\%AppName%\” %AppParams%”
作为当前目录:
nssm set %SrvName% AppDirectory %AppDir% ornssm set %SrvName% AppDirectory \”%AppDir%\”
如果配置不正确,启动服务时可能会遇到以下错误:
(无法在本地计算机上启动TestService。有关详细信息,请查看系统事件日志。如果这是非 Microsoft 服务,请联系服务供应商,并引用特定于服务的错误代码3。)
前往系统事件日志查看详情:
(服务控制管理器:错误:由于内部错误,服务“TestService”已停止:系统找不到指定的路径。)
查看注册表路径有什么问题:
根据结果得出结论,是在正确的位置多余/缺失引号或反斜杠。
结果脚本如下:
@echo onset WrapperPath=”E:\Java\JavaPrograms\service\nssm.exe”set SrvName=”TestServ”set SrvDispName=”Test Service”set PathToProg=”E:\Java\JavaPrograms\jdk-11.0.1\bin\java.exe”set ProgParams=”-Xmx400m”set AppDir=”E:\Java\JavaPrograms\test\with Spaces”set AppName=”mainTutorial-1.0-SNAPSHOT.jar”set AppParams=\”amazing\” \”prod\”%WrapperPath% stop %SrvName%%WrapperPath% remove %SrvName% confirm%WrapperPath% install %SrvName% %PathToProg% “%ProgParams% -jar \”%AppDir%\\%AppName%\” %AppParams%”%WrapperPath% set %SrvName% DisplayName %SrvDispName%%WrapperPath% set %SrvName% AppDirectory %AppDir%%WrapperPath% set %SrvName% AppRestartDelay 1000%WrapperPath% set %SrvName% Description “Start %PathToProg% with params %ProgParams% -jar \”%AppDir%\\%AppName%\” %AppParams%”
而在注册表中的参数中:
让我们测试重启。使用并行线程运行带有异常抛出的应用程序,使应用程序在两次迭代后终止自身。
set AppParams=\”amazing\” \”prod\” 2
观察日志文件,看到它们被重新创建,这意味着服务在完成后重新启动了我们的应用程序。太棒了!即使日志在系统事件日志中呈现,其来源为_nssm_:
Program E:\Java\JavaPrograms\jdk-11.0.1\bin\java.exe for service TestServ exited with return code 0.Killing process tree of process 11196 for service TestServ with exit code 0Service TestServ action for exit code 0 is Restart. Attempting to restart E:\Java\JavaPrograms\jdk-11.0.1\bin\java.exe.
服务TestServ运行时间少于1500毫秒。重启将延迟2000毫秒。
结论:自动化你需要的一切,而不是你能够做的一切,否则可能需要更多时间来进行无用的工作。
4) 奖励: jar转exe
现在让我们尝试将我们的jar文件转换/包装/推入exe文件并将其作为服务运行。
这里有几个变种:
A) JDK工具,即JavaPackager(JDK8,JDK10,JDK11)。
我还没有尝试过,那里有很多机会,但对于我们现在的目的来说有点混乱。
B) 从stackoverflow中编译。
C) Launch4j - 独立程序或作为Maven插件。
使用独立的launch4j。
填写GUI表单并指定jar的路径及其参数,尝试将JDK嵌入其中:
但是,它无法启动,“启动”按钮未高亮显示。怎么了?尝试保存设置并从cmd运行它:
launch4jc.exe “E:\Java\JavaPrograms\test\with Spaces\exe\mainTutL4j.xml”
运行,得到:
Error: A JNI error has occurred, please check your installation and try againException in thread “main” java.lang.UnsupportedClassVersionError: net/snatchTech/tutorials/App has been compiled by a more recent version of the Java Runtime (class file version 55.0), this version of the Java Runtime only recognizes class file versions up to 52.0
哦,是的,它在屏幕截图上看不到,但我首先玩了一下,将jre最大版本指定为9,但它需要是11。删除jre版本,运行,得到:
some error: This application was configured to use a bundled Java Runtime Environment but the runtime is missing or corrupted.
额..在Boundled JRE Path中添加“\javaw.exe”。不起作用。删除javaw和bin文件夹。哇!但是等等,现在运行exe文件:
E:\Java\JavaPrograms\test\with Spaces\exe>mainTut.exeError: Unable to initialize main class net.snatchTech.tutorials.AppCaused by: java.lang.NoClassDefFoundError: com/fasterxml/jackson/core/type/TypeReference
啊..它必须是一个fat jar..好吧,移动资源文件夹。启动..是的,它起作用了!
从运行的进程中可以看出,在我们的情况下,mainTut.exe只是一个包装器,它使用参数启动java-exe –jar。此外,在“jar文件路径”参数中是mainTut.exe本身!让我们尝试使用存档程序打开exe文件:
实际上,这里与我们的jar文件中的内容相同。
一点需要注意的是,我们据称内置了JRE,尽管它的大小和内部存档并不明显。让我们尝试重命名jdk文件夹进行验证。也就是说,它不起作用。我们将不会被所有关系所折磨,重要的是——这是一种工作的变体,必须将其作为服务进行测试。
sc create TestServ4 binPath= “E:\Java\JavaPrograms\test\with Spaces\exe\mainTut.exe” type= own start= auto error= normal DisplayName= TestService4
但结果令人失望,就像从cmd中一样——mainTut.exe关闭,但java.exe仍然存在。+必须以某种方式将不同的参数传递给jar,因为它们被缝合到exe文件中。``` 结论:仅仅将你的jar文件封装成exe文件并不能让你获得将其作为服务运行的好处。根本没有好处。这种封装更适合用于组装安装包。
总结
使用NSSM将jar文件作为服务启动,享受稳定运行时间吧!
译自:https://medium.com/@lk.snatch/jar-file-as-windows-service-bonus-jar-to-exe-1b7b179053e4
评论(0)