有时候我们希望我们java写的程序作为服务注册到系统中,Java Service Wrapper(下面简称wrapper)是目前较为流行的将Java程序部署成Windows服务的解决方案, 本文将讨论如何使用wrapper把我们的程序打包成WIN服务!
主要作用有:
1.打包服务
2.设置JVM参数
3.所有的日志可以输出到指定文件
0.准备需要注册为服务的程序
public class MapTest { private static int i; public static void main(String[] args) { while (true) { try { System.out.println("访问次数:" + i++); HttpUtil.doGet("http://www.cnblogs.com/qlqwjy/"); Thread.sleep(2 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } } }}
上面程序依赖的jar包:
将上面程序也打成包:(使用eclipse打包或者直接Jdk自带的jar打包)
1.下载serviceWrapper包
下载地址:
下载后是一个压缩包,解压目录如下:
2.开始注册一个简单的服务:
1. 准备一个目录,例如我在桌面建了一个SW目录,并在里面新建如下结构的目录: 接下来全文的%EXAMPLE_HOME% 就是我新建的SW目录名称
%EXAMPLE_HOME%\%EXAMPLE_HOME%\bin\%EXAMPLE_HOME%\conf\%EXAMPLE_HOME%\lang\%EXAMPLE_HOME%\lib\%EXAMPLE_HOME%\mylib\%EXAMPLE_HOME%\logs\
如下:lang目录是存放支持其他语言的语言包,一般用不到
2. 然后将我们下载的wrapper目录下的文件拷贝到我们上面建的目录:
%WRAPPER_HOME%\bin\wrapper.exe -> %EXAMPLE_HOME%\bin\wrapper.exe%WRAPPER_HOME%\lib\wrapper.jar -> %EXAMPLE_HOME%\lib\wrapper.jar
%WRAPPER_HOME%\lib\wrapper.dll -> %EXAMPLE_HOME%\lib\wrapper.dll
%WRAPPER_HOME%\conf\wrapper.conf -> %EXAMPLE_HOME%\conf\wrapper.conf 将自己程序打成的包以及自己程序依赖的包放到mylib:
3.修改配置文件 %EXAMPLE_HOME%\conf\wrapper.conf
#java.exe所在位置 wrapper.java.command=C:\Program Files\Java\jdk1.7.0_80\bin\java.exe #日志级别wrapper.java.command.loglevel=INFO #主类入口,第一个mainClass是固定写法,是wrapper自带的,不可以写成自己的,如果写成自己的入口程序自己的程序需要实现wrapper的WrapperListener接口 #parameter.1是自己的主程序入口所在类(从包名开始)wrapper.java.mainclass=org.tanukisoftware.wrapper.WrapperSimpleAppwrapper.app.parameter.1=MapTest #依赖的包,第一个是wrapper包,第二个是自己打的包以及程序依赖包wrapper.java.classpath.1=../lib/wrapper.jarwrapper.java.classpath.2=../mylib/*.jar #固定写法,依赖的wrapper的包wrapper.java.library.path.1=../lib #日志文件位置wrapper.logfile=../logs/wrapper.log #服务名称以及描述信息wrapper.console.title=Hello World Serverwrapper.name=helloworldserverwrapper.displayname=Hello World Serverwrapper.description=Hello World Server
注意:
(1)上面的主类实际是:org.tanukisoftware.wrapper.WrapperSimpleApp,此类实现了WrapperListener接口。所以如果我们用此参数定义自己的主函数需要实现此接口。实现此接口就不用wrapper.app.parameter.1作为函数参数了
(2)wrapper.app.parameter.1=MapTest 是将Maptest作为参数传入到主函数中,也就是依次作为类WrapperSimpleApp的main(String[] args)函数的参数。源码如下: (3)不能一次传多个参数,生效的始终是第一个参数,传第二个参数也不会生效。传第二个参数或者多个参数是作为MapTest的主函数的参数。
package org.tanukisoftware.wrapper;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.lang.reflect.Modifier;import org.tanukisoftware.wrapper.WrapperListener;import org.tanukisoftware.wrapper.WrapperManager;import org.tanukisoftware.wrapper.WrapperPrintStream;import org.tanukisoftware.wrapper.WrapperSystemPropertyUtil;public class WrapperSimpleApp implements WrapperListener, Runnable { private static WrapperPrintStream m_outInfo; private static WrapperPrintStream m_outError; private static WrapperPrintStream m_outDebug; private Method m_mainMethod; private String[] m_appArgs; private boolean m_mainStarted; private boolean m_mainComplete; private Integer m_mainExitCode; private boolean m_ignoreMainExceptions; private boolean m_startComplete; protected WrapperSimpleApp(String[] args) { if (class$org$tanukisoftware$wrapper$WrapperManager == null) { class$org$tanukisoftware$wrapper$WrapperManager = class$("org.tanukisoftware.wrapper.WrapperManager"); } else { Class arg9999 = class$org$tanukisoftware$wrapper$WrapperManager; } this.m_mainMethod = null; m_outInfo = new WrapperPrintStream(System.out, "WrapperSimpleApp: "); m_outError = new WrapperPrintStream(System.out, "WrapperSimpleApp Error: "); m_outDebug = new WrapperPrintStream(System.out, "WrapperSimpleApp Debug: "); if (args.length < 1) { this.showUsage(); WrapperManager.stop(1); } else { String mainClassString = args[0]; String mainMethodString = "main"; String[] ar = args[0].split("/"); if (ar.length > 1) { mainClassString = ar[0]; mainMethodString = ar[1]; } Class mainClass; try { mainClass = Class.forName(mainClassString); } catch (ClassNotFoundException arg11) { m_outError.println(WrapperManager.getRes().getString("Unable to locate the class {0} : {1}", mainClassString, arg11)); this.showUsage(); WrapperManager.stop(1); return; } catch (ExceptionInInitializerError arg12) { m_outError.println(WrapperManager.getRes() .getString("Class {0} found but could not be initialized due to:", mainClassString)); arg12.printStackTrace(m_outError); WrapperManager.stop(1); return; } catch (LinkageError arg13) { m_outError.println(WrapperManager.getRes() .getString("Class {0} found but could not be initialized: {1}", mainClassString, arg13)); WrapperManager.stop(1); return; } try { this.m_mainMethod = mainClass.getMethod(mainMethodString, new Class[]{String[].class}); } catch (NoSuchMethodException arg9) { try { this.m_mainMethod = mainClass.getMethod(mainMethodString, new Class[0]); } catch (NoSuchMethodException arg8) { ; } if (this.m_mainMethod == null) { m_outError.println(WrapperManager.getRes().getString( "Unable to locate a public static {2} method in class {0} : {1}", mainClassString, arg9, mainMethodString)); this.showUsage(); WrapperManager.stop(1); return; } } catch (SecurityException arg10) { m_outError.println(WrapperManager.getRes().getString( "Unable to locate a public static {2} method in class {0} : {1}", mainClassString, arg10, mainMethodString)); this.showUsage(); WrapperManager.stop(1); return; } int modifiers = this.m_mainMethod.getModifiers(); if (Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers)) { String[] appArgs = new String[args.length - 1]; System.arraycopy(args, 1, appArgs, 0, appArgs.length); WrapperManager.start(this, appArgs); } else { m_outError.println(WrapperManager.getRes().getString( "The {1} method in class {0} must be declared public and static.", mainClassString, mainMethodString)); this.showUsage(); WrapperManager.stop(1); } } } public void run() { synchronized (this) { this.m_mainStarted = true; this.notifyAll(); } Object t = null; try { if (WrapperManager.isDebugEnabled()) { m_outDebug.println(WrapperManager.getRes().getString("invoking main method")); } try { this.m_mainMethod.invoke((Object) null, new Object[]{ this.m_appArgs}); } catch (IllegalArgumentException arg15) { this.m_mainMethod.invoke((Object) null, new Object[0]); } finally { Thread.currentThread().setPriority(10); } if (WrapperManager.isDebugEnabled()) { m_outDebug.println(WrapperManager.getRes().getString("main method completed")); } synchronized (this) { this.m_mainComplete = true; this.notifyAll(); return; } } catch (IllegalAccessException arg18) { t = arg18; } catch (IllegalArgumentException arg19) { t = arg19; } catch (InvocationTargetException arg20) { t = arg20.getTargetException(); if (t == null) { t = arg20; } } m_outInfo.println(); m_outError.println(WrapperManager.getRes().getString("Encountered an error running main:")); ((Throwable) t).printStackTrace(m_outError); synchronized (this) { if (this.m_ignoreMainExceptions) { if (!this.m_startComplete) { this.m_mainComplete = true; this.notifyAll(); } } else if (this.m_startComplete) { WrapperManager.stop(1); } else { this.m_mainComplete = true; this.m_mainExitCode = new Integer(1); this.notifyAll(); } } } public Integer start(String[] args) { boolean waitForStartMain = WrapperSystemPropertyUtil .getBooleanProperty(WrapperSimpleApp.class.getName() + ".waitForStartMain", false); this.m_ignoreMainExceptions = WrapperSystemPropertyUtil .getBooleanProperty(WrapperSimpleApp.class.getName() + ".ignoreMainExceptions", false); int maxStartMainWait = WrapperSystemPropertyUtil .getIntProperty(WrapperSimpleApp.class.getName() + ".maxStartMainWait", 2); maxStartMainWait = Math.max(1, maxStartMainWait); int maxLoops; if (waitForStartMain) { maxLoops = Integer.MAX_VALUE; if (WrapperManager.isDebugEnabled()) { m_outDebug.println(WrapperManager.getRes() .getString("start(args) Will wait indefinitely for the main method to complete.")); } } else { maxLoops = maxStartMainWait; if (WrapperManager.isDebugEnabled()) { m_outDebug.println(WrapperManager.getRes().getString( "start(args) Will wait up to {0} seconds for the main method to complete.", new Integer(maxStartMainWait))); } } Thread mainThread = new Thread(this, "WrapperSimpleAppMain"); synchronized (this) { this.m_appArgs = args; mainThread.start(); Thread.currentThread().setPriority(10); while (!this.m_mainStarted) { try { this.wait(1000L); } catch (InterruptedException arg10) { ; } } for (int loops = 0; loops < maxLoops && !this.m_mainComplete; ++loops) { try { this.wait(1000L); } catch (InterruptedException arg9) { ; } if (!this.m_mainComplete) { WrapperManager.signalStarting(5000); } } this.m_startComplete = true; if (WrapperManager.isDebugEnabled()) { m_outDebug .println(WrapperManager.getRes().getString("start(args) end. Main Completed={0}, exitCode={1}", new Boolean(this.m_mainComplete), this.m_mainExitCode)); } return this.m_mainExitCode; } } public int stop(int exitCode) { if (WrapperManager.isDebugEnabled()) { m_outDebug.println(WrapperManager.getRes().getString("stop({0})", new Integer(exitCode))); } return exitCode; } public void controlEvent(int event) { if (event != 202 || !WrapperManager.isLaunchedAsService() && !WrapperManager.isIgnoreUserLogoffs()) { if (WrapperManager.isDebugEnabled()) { m_outDebug.println(WrapperManager.getRes().getString("controlEvent({0}) Stopping", new Integer(event))); } WrapperManager.stop(0); } else { m_outInfo.println(WrapperManager.getRes().getString("User logged out. Ignored.")); } } protected void showUsage() { System.out.println(); System.out.println(WrapperManager.getRes().getString("WrapperSimpleApp Usage:")); System.out.println(WrapperManager.getRes().getString( " java org.tanukisoftware.wrapper.WrapperSimpleApp {app_class{/app_method}} [app_arguments]")); System.out.println(); System.out.println(WrapperManager.getRes().getString("Where:")); System.out.println(WrapperManager.getRes() .getString(" app_class: The fully qualified class name of the application to run.")); System.out.println(WrapperManager.getRes() .getString(" app_arguments: The arguments that would normally be passed to the")); System.out.println(WrapperManager.getRes().getString(" application.")); } public static void main(String[] args) { new WrapperSimpleApp(args); }}
4.开始注册服务以及测试
(1)控制台测试
C:\Users\Administrator>cd C:\Users\Administrator\Desktop\SWC:\Users\Administrator\Desktop\SW>bin\wrapper.exe -c ..\conf\wrapper.confwrapper | --> Wrapper Started as Consolewrapper | Java Service Wrapper Community Edition 32-bit 3.5.25wrapper | Copyright (C) 1999-2014 Tanuki Software, Ltd. All Rights Reserved.wrapper | http://wrapper.tanukisoftware.comwrapper |wrapper | Launching a JVM...wrapper | Java Command Line:wrapper | Command: "C:\Program Files\Java\jdk1.7.0_80\bin\java.exe" -Djava.library.path="../lib" -classpath "../lib/wrapper.jar;../mylib/commons-logging-1.0.4.jar;../mylib/commons.jar;../mylib/httpclient-4.3.1.jar;../mylib/httpcore-4.3.jar;../mylib/httpmime-4.3.1.jar;../mylib/log4j-1.2.12.jar" -Dwrapper.key="2azws1iyaXYR9r26" -Dwrapper.port=32000 -Dwrapper.jvm.port.min=31000 -Dwrapper.jvm.port.max=31999 -Dwrapper.pid=21292 -Dwrapper.version="3.5.25" -Dwrapper.native_library="wrapper" -Dwrapper.arch="x86" -Dwrapper.cpu.timeout="10" -Dwrapper.jvmid=1 org.tanukisoftware.wrapper.WrapperSimpleApp MapTestjvm 1 | WrapperManager: Initializing...jvm 1 | WrapperManager:jvm 1 | WrapperManager: WARNING - Unable to load the Wrapper''s native library 'wrapper.dll'.jvm 1 | WrapperManager: The file is located on the path at the following location butjvm 1 | WrapperManager: could not be loaded:jvm 1 | WrapperManager: C:\Users\Administrator\Desktop\SW\bin\..\lib\wrapper.dlljvm 1 | WrapperManager: Please verify that the file is both readable and executable by thejvm 1 | WrapperManager: current user and that the file has not been corrupted in any way.jvm 1 | WrapperManager: One common cause of this problem is running a 32-bit versionjvm 1 | WrapperManager: of the Wrapper with a 64-bit version of Java, or vica versa.jvm 1 | WrapperManager: This is a 64-bit JVM.jvm 1 | WrapperManager: Reported cause:jvm 1 | WrapperManager: C:\Users\Administrator\Desktop\SW\lib\wrapper.dll: Can't load IA 32-bit .dll on a AMD 64-bit platformjvm 1 | WrapperManager: System signals will not be handled correctly.jvm 1 | WrapperManager:jvm 1 | 访问次数:0jvm 1 | log4j:WARN No appenders could be found for logger (org.apache.http.impl.conn.BasicClientConnectionManager).jvm 1 | log4j:WARN Please initialize the log4j system properly.jvm 1 | 访问次数:1jvm 1 | 访问次数:2jvm 1 | 访问次数:3
(2)将程序注册为服务:
bin\wrapper.exe -i ..\conf\wrapper.conf
结果:
wrapper | Hello World Server service installed.
(3)启动服务:
net start helloworld
或者:
bin\wrapper.exe -t ..\conf\wrapper.conf
启动之后我们回看到程序输出在logs\wrapper.log文件内:
(4)停止服务
net stop helloword
或者
bin\wrapper.exe -p ..\conf\wrapper.conf
(5)删除服务
sc delete helloword
或者
bin\wrapper.exe -r ..\conf\wrapper.conf
-----------上面是在系统有Java环境的情况下的设置,现在假设我们不存在Java运行环境,也就是没有JRE与JDK:-------------
(1)拷贝java安装目录下的JRE(包含bin目录和相关lib)目录到上面的目录%EXAMPLE_HOME%,如下:
jre目录下:
(2)修改配置文件,利用我们上面的jre目录下的jar包和bin\java.exe
#java.exe所在位置wrapper.java.command=../jre/bin/java.exe#日志级别wrapper.java.command.loglevel=INFO#主类入口,第一个mainClass是固定写法,是wrapper自带的,不可以写成自己的,如果写成自己的入口程序自己的程序需要实现wrapper的WrapperListener接口wrapper.java.mainclass=org.tanukisoftware.wrapper.WrapperSimpleApp#parameter.1是自己的主程序入口所在类(从包名开始)wrapper.app.parameter.1=MapTest#依赖的包,第一个是wrapper包,第二个是自己打的包以及程序依赖包wrapper.java.classpath.1=../jre/lib/*.jarwrapper.java.classpath.2=../lib/wrapper.jarwrapper.java.classpath.3=../mylib/*.jar#固定写法,依赖的wrapper的包wrapper.java.library.path.1=../lib#日志文件位置wrapper.logfile=../logs/wrapper.log#服务名称以及描述信息wrapper.console.title=Hello World Serverwrapper.name=helloworldserverwrapper.displayname=Hello World Serverwrapper.description=Hello World Server
(3)安装服务与测试与上面一样:
C:\Users\liqiang\Desktop\xxx\SW>bin\wrapper.exe -c ..\conf\wrapper.confwrapper | --> Wrapper Started as Consolewrapper | Java Service Wrapper Community Edition 32-bit 3.5.25wrapper | Copyright (C) 1999-2014 Tanuki Software, Ltd. All Rights Reserved.wrapper | http://wrapper.tanukisoftware.comwrapper |wrapper | Launching a JVM...wrapper | Java Command Line:wrapper | Command: "..\jre\bin\java.exe" -Djava.library.path="../lib" -classparsets.jar;../jre/lib/deploy.jar;../jre/lib/javaws.jar;../jre/lib/jce.jar;../jre/lib/jsse.jar;../jre/lib/management-agent.jar;../jre/lib/plugin.jar;../jre/lib/rapper.jar;../mylib/commons-logging-1.0.4.jar;../mylib/commons.jar;../mylib/httpcr;../mylib/httpmime-4.3.1.jar;../mylib/log4j-1.2.12.jar" -Dwrapper.key="408rjGp1.jvm.port.min=31000 -Dwrapper.jvm.port.max=31999 -Dwrapper.pid=336144 -Dwrapper.y="wrapper" -Dwrapper.arch="x86" -Dwrapper.cpu.timeout="10" -Dwrapper.jvmid=1 orp MapTestjvm 1 | WrapperManager: Initializing...jvm 1 | WrapperManager:jvm 1 | WrapperManager: WARNING - Unable to load the Wrapper''s native librarjvm 1 | WrapperManager: The file is located on the path at the folljvm 1 | WrapperManager: could not be loaded:jvm 1 | WrapperManager: C:\Users\liqiang\Desktop\xxx\SW\bin\..\lijvm 1 | WrapperManager: Please verify that the file is both readabljvm 1 | WrapperManager: current user and that the file has not beenjvm 1 | WrapperManager: One common cause of this problem is runningjvm 1 | WrapperManager: of the Wrapper with a 64-bit version of Javjvm 1 | WrapperManager: This is a 64-bit JVM.jvm 1 | WrapperManager: Reported cause:jvm 1 | WrapperManager: C:\Users\liqiang\Desktop\xxx\SW\lib\wrappMD 64-bit platformjvm 1 | WrapperManager: System signals will not be handled correctljvm 1 | WrapperManager:jvm 1 | 访问次数:0jvm 1 | log4j:WARN No appenders could be found for logger (org.apache.http.imjvm 1 | log4j:WARN Please initialize the log4j system properly.jvm 1 | 访问次数:1wrapper | CTRL-C trapped. Shutting down.wrapper | <-- Wrapper StoppedC:\Users\liqiang\Desktop\xxx\SW>bin\wrapper.exe -i ..\conf\wrapper.confwrapperm | Hello World Server service installed.C:\Users\liqiang\Desktop\xxx\SW>java'java' 不是内部或外部命令,也不是可运行的程序或批处理文件。C:\Users\liqiang\Desktop\xxx\SW>javac'javac' 不是内部或外部命令,也不是可运行的程序或批处理文件。
为了验证我们的服务是使用的jre目录下的JDK,我们可以将jre目录删掉进行测试,或者是将配置文件中jre\lib的引入注释掉进行测试。
现在我们将jre目录删掉,启动服务报错如下:
在wrapper.log中查看到的日志文件如下:
至此我们实现了简单的有JRE与无JRE两种情况的注册服务,实际没有JRE环境的时候我们只需要将我们的JRE附到目录中并且在wrapper.conf中指明所在路径即可。
补充1.:上面配置会导致JVM不断重启,需要加JVM参数以及设置,同时设置服务开机启动:并且使用一变量记住我们的项目路径;修改wrapper.conf并且删掉服务重新添加:
#定义了一个根路径,注意set和.之间没有空格,下面就可以用%basePath%取此变量set.basePath=C:\Users\liqiang\Desktop\xxx\SW#java.exe所在位置wrapper.java.command=%basePath%/jre/bin/java.exe#日志级别wrapper.java.command.loglevel=INFO#主类入口,第一个mainClass是固定写法,是wrapper自带的,不可以写成自己的,如果写成自己的入口程序自己的程序需要实现wrapper的WrapperListener接口wrapper.java.mainclass=org.tanukisoftware.wrapper.WrapperSimpleApp#parameter.1是自己的主程序入口所在类(从包名开始)wrapper.app.parameter.1=MapTest#依赖的包,第一个是wrapper包,第二个是自己打的包以及程序依赖包wrapper.java.classpath.1=../jre/lib/*.jarwrapper.java.classpath.2=../lib/wrapper.jarwrapper.java.classpath.3=../mylib/*.jar#固定写法,依赖的wrapper的包wrapper.java.library.path.1=../lib#日志文件位置wrapper.logfile=../logs/wrapper.log#服务名称以及描述信息wrapper.console.title=Hello World Serverwrapper.name=helloworldserverwrapper.displayname=Hello World Serverwrapper.description=Hello World Serverwrapper.jmx=falsewrapper.on_exit.0=SHUTDOWNwrapper.on_exit.default=RESTARTwrapper.ntservice.interactive = true#服务开机启动wrapper.ntservice.starttype=AUTO_STARTwrapper.tray = truewrapper.java.monitor.deadlock = truewrapper.java.monitor.heap = truewrapper.java.monitor.gc.restart = true# Java Heap 初始化大小(单位:MB)wrapper.java.initmemory=128# Java Heap 最大值(单位:MB)wrapper.java.maxmemory=128
删除重装服务:
C:\Users\liqiang\Desktop\xxx\SW>bin\wrapper.exe -r ..\conf\wrapper.confwrapperm | Hello World Server service removed.
C:\Users\liqiang\Desktop\xxx\SW>bin\wrapper.exe -c ..\conf\wrapper.confwrapper | --> Wrapper Started as Consolewrapper | Java Service Wrapper Community Edition 32-bit 3.5.25wrapper | Copyright (C) 1999-2014 Tanuki Software, Ltd. All Rights Reserved.wrapper | http://wrapper.tanukisoftware.comwrapper |wrapper | Launching a JVM...wrapper | Java Command Line:wrapper | Command: "C:\Users\liqiang\Desktop\xxx\SW\jre\bin\java.exe" -Xms128-classpath "../jre/lib/alt-rt.jar;../jre/lib/charsets.jar;../jre/lib/deploy.jar;;../jre/lib/jfr.jar;../jre/lib/jfxrt.jar;../jre/lib/jsse.jar;../jre/lib/managemee/lib/resources.jar;../jre/lib/rt.jar;../lib/wrapper.jar;../mylib/commons-logginb/httpclient-4.3.1.jar;../mylib/httpcore-4.3.jar;../mylib/httpmime-4.3.1.jar;../P7sqJIaMu25Avke" -Dwrapper.port=32000 -Dwrapper.jvm.port.min=31000 -Dwrapper.jvmrapper.version="3.5.25" -Dwrapper.native_library="wrapper" -Dwrapper.arch="x86"id=1 org.tanukisoftware.wrapper.WrapperSimpleApp MapTestjvm 1 | WrapperManager: Initializing...jvm 1 | WrapperManager:jvm 1 | WrapperManager: WARNING - Unable to load the Wrapper''s native librarjvm 1 | WrapperManager: The file is located on the path at the folljvm 1 | WrapperManager: could not be loaded:jvm 1 | WrapperManager: C:\Users\liqiang\Desktop\xxx\SW\bin\..\lijvm 1 | WrapperManager: Please verify that the file is both readabljvm 1 | WrapperManager: current user and that the file has not beenjvm 1 | WrapperManager: One common cause of this problem is runningjvm 1 | WrapperManager: of the Wrapper with a 64-bit version of Javjvm 1 | WrapperManager: This is a 64-bit JVM.jvm 1 | WrapperManager: Reported cause:jvm 1 | WrapperManager: C:\Users\liqiang\Desktop\xxx\SW\lib\wrappMD 64-bit platformjvm 1 | WrapperManager: System signals will not be handled correctljvm 1 | WrapperManager:jvm 1 | 访问次数:0wrapper | CTRL-C trapped. Shutting down.jvm 1 | log4j:WARN No appenders could be found for logger (org.apache.http.imjvm 1 | log4j:WARN Please initialize the log4j system properly.wrapper | <-- Wrapper StoppedC:\Users\liqiang\Desktop\xxx\SW>bin\wrapper.exe -i ..\conf\wrapper.confwrapperm | Hello World Server service installed.
如果我们想检测JVM参数可以用jps+jmap监测即可:
C:\Users\liqiang>jps505668 Jps504320 WrapperSimpleApp487768C:\Users\liqiang>jmap -heap 504320Attaching to process ID 504320, please wait...Debugger attached successfully.Server compiler detected.JVM version is 24.80-b11using thread-local object allocation.Parallel GC with 4 thread(s)Heap Configuration: MinHeapFreeRatio = 0 MaxHeapFreeRatio = 100 MaxHeapSize = 134217728 (128.0MB) NewSize = 1310720 (1.25MB) MaxNewSize = 17592186044415 MB OldSize = 5439488 (5.1875MB) NewRatio = 2 SurvivorRatio = 8 PermSize = 21757952 (20.75MB) MaxPermSize = 85983232 (82.0MB) G1HeapRegionSize = 0 (0.0MB)Heap Usage:PS Young GenerationEden Space: capacity = 34603008 (33.0MB) used = 9710016 (9.26019287109375MB) free = 24892992 (23.73980712890625MB) 28.06119051846591% usedFrom Space: capacity = 5242880 (5.0MB) used = 0 (0.0MB) free = 5242880 (5.0MB) 0.0% usedTo Space: capacity = 5242880 (5.0MB) used = 0 (0.0MB) free = 5242880 (5.0MB) 0.0% usedPS Old Generation capacity = 89653248 (85.5MB) used = 0 (0.0MB) free = 89653248 (85.5MB) 0.0% usedPS Perm Generation capacity = 22020096 (21.0MB) used = 6630336 (6.32318115234375MB) free = 15389760 (14.67681884765625MB) 30.110386439732142% used3336 interned Strings occupying 269976 bytes.
补充2:我们可以用上面的service wrapper的bin包中的bat文件进行安装服务: (将上面安下载解压后的wrapper-windows-x86-32-3.5.25\bin中的文件复制到我们的%EXAMPLE_HOME%\bin目录下)
我们只需要点击上面的bat文件即可实现上面的操作。
*************************
我已经将我的一个例子上传到我的服务器,下载地址:
下载解压之后只需要将自己程序打的包以及依赖包放到mylib目录下,修改conf\wrapper.conf文件中下面两处
set.basePath=C:\Users\liqiang\Desktop\xxx\SW #替换为自己的目录
#parameter.1是自己的主程序入口所在类(从包名开始)wrapper.app.parameter.1=MapTest #替换为自己的程序入口
然后点击bin目录的TestWrapper.bat测试即可
**************************
3.将log4j日志与servicewrapper日志进行整合
开发环境下一切正常。用JavaServiceWrapper部署到服务器上之后,发现log文件没有生成。同时在wrapper的log中有两行log4j的错误信息:
log4j:WARN No appenders could be found for logger (com.xxxxx).
log4j:WARN Please initialize the log4j system properly.
查找了一番,最后发现在wrapper.conf中加入一行,硬性指明log4j的配置文件就OK了:
wrapper.java.additional.1=-Dlog4j.configuration=file:../conf/log4j.properties
StackOverflow上有人回答类似的问题,用的方法是
wrapper.java.additional.1=-Dlog4j.configuration=../config/log4j.properties
但是我这样测试没成功。不知道是不是和版本有关。
(1) 测试代码:
package serviceWrapper;import org.slf4j.Logger;import org.slf4j.LoggerFactory;public class MapTest { private static Logger log = LoggerFactory.getLogger(MapTest.class); public static void main(String[] args) { int times = 0; while (true) { try { Thread.sleep(3 * 1000); } catch (InterruptedException e) { } try { int i = 1 / 0; } catch (Exception e) { log.error("error[{}] by / 0 ", times++, e); } } }}
(2)测试代码打成包并一起复制到mylib目录下:
依赖的jar包以及自己打的包:
(3)目录结构:
(4)bin目录与上面一样,jre也一样,lang也一样,lib也一样,logs也一样,mylib在(2)中换过,将log4j.properties文件复制到conf目录下:
log4j.properties文件:
log4j.rootLogger=info,A,Blog4j.appender.A=org.apache.log4j.ConsoleAppenderlog4j.appender.A.layout=org.apache.log4j.PatternLayoutlog4j.appender.A.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%c]-[%p] %m%nlog4j.appender.B=org.apache.log4j.RollingFileAppenderlog4j.appender.B.File=E:\\test.loglog4j.appender.B.MaxFileSize=10MBlog4j.appender.B.MaxBackupIndex=5log4j.appender.B.layout=org.apache.log4j.PatternLayoutlog4j.appender.B.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%c]-[%p] %m%n
(5)修改wrapper.conf文件:
#定义了一个根路径,注意set和.之间没有空格,下面就可以用%basePath%取此变量set.basePath=C:\Users\liqiang\Desktop\logTest#java.exe所在位置wrapper.java.command=%basePath%/jre/bin/java.exe#主类入口,第一个mainClass是固定写法,是wrapper自带的,不可以写成自己的,如果写成自己的入口程序自己的程序需要实现wrapper的WrapperListener接口wrapper.java.mainclass=org.tanukisoftware.wrapper.WrapperSimpleApp#parameter.1是自己的主程序入口所在类(从包名开始),生效的始终是第一个参数wrapper.app.parameter.1=serviceWrapper.MapTestwrapper.app.parameter.2=MapTest# Java Classpath配置,必须从序号"1"开始,添加新的jar包后序号递增#JRE的包wrapper.java.classpath.1=../jre/lib/*.jar#wrapper.jarwrapper.java.classpath.2=../lib/wrapper.jar#自己的包以及程序依赖包wrapper.java.classpath.3=../mylib/*.jar#固定写法,依赖的wrapper的包wrapper.java.library.path.1=%basePath%/lib#日志文件位置wrapper.logfile=../logs/wrapper.log# 控制台信息输出格式wrapper.console.format=PM# 日志文件输出格式wrapper.logfile.format=LPTM# 日志文件日志级别wrapper.logfile.loglevel=INFO#服务名称以及描述信息wrapper.console.title=Hello World Serverwrapper.name=helloworldserverwrapper.displayname=Hello World Serverwrapper.description=Hello World Serverwrapper.jmx=falsewrapper.on_exit.0=SHUTDOWNwrapper.on_exit.default=RESTARTwrapper.ntservice.interactive = true#服务开机启动wrapper.ntservice.starttype=AUTO_STARTwrapper.tray = truewrapper.java.monitor.deadlock = truewrapper.java.monitor.heap = truewrapper.java.monitor.gc.restart = true# Java Heap 初始化大小(单位:MB)wrapper.java.initmemory=128# Java Heap 最大值(单位:MB)wrapper.java.maxmemory=128# 32/64位选择,true为自动选择wrapper.java.additional.auto_bits=TRUE#附加参数即为java命令可选参数,如下所示:#设置log4J日志文件位置wrapper.java.additional.1=-Dlog4j.configuration=file:../conf/log4j.properties
注意: 附加参数 wrapper.java.additional.1 即为java命令可选参数(可用于指定JVM参数与指定垃圾收集器组合),如下所示:
C:\Users\liqiang>java用法: java [-options] class [args...] (执行类) 或 java [-options] -jar jarfile [args...] (执行 jar 文件)其中选项包括: -d32 使用 32 位数据模型 (如果可用) -d64 使用 64 位数据模型 (如果可用) -server 选择 "server" VM -hotspot 是 "server" VM 的同义词 [已过时] 默认 VM 是 server. -cp <目录和 zip jar 文件的类搜索路径> -classpath <目录和 zip jar 文件的类搜索路径> 用 ; 分隔的目录, JAR 档案 和 ZIP 档案列表, 用于搜索类文件。 -D <名称> = <值> 设置系统属性 -verbose:[class|gc|jni] 启用详细输出 -version 输出产品版本并退出 -version: <值> 需要指定的版本才能运行 -showversion 输出产品版本并继续 -jre-restrict-search | -no-jre-restrict-search 在版本搜索中包括/排除用户专用 JRE -? -help 输出此帮助消息 -X 输出非标准选项的帮助 -ea[:...|: 值> 值> 名称> 目录和> 目录和>] -enableassertions[: ...|: ] 按指定的粒度启用断言 -da[: ...|: ] -disableassertions[: ...|: ] 禁用具有指定粒度的断言 -esa | -enablesystemassertions 启用系统断言 -dsa | -disablesystemassertions 禁用系统断言 -agentlib: [= <选项> ] 加载本机代理库 , 例如 -agentlib:hprof 另请参阅 -agentlib:jdwp=help 和 -agentlib:hprof=help -agentpath: 选项>[= <选项> ] 按完整路径名加载本机代理库 -javaagent: [= <选项> ] 加载 Java 编程语言代理, 请参阅 java.lang.instrument -splash: 选项>使用指定的图像显示启动屏幕有关详细信息, 请参阅 http://www.oracle.com/technetwork/java/javase/documentation 选项>
(6)点击bin目录下的TestWrapper.bat测试控制台:
logs\wrapper.log文件内容如下:
E:\test.log文件内容如下:
log4j生成的日志文件与wrapper.logs文件的内容几乎一样,只是格式不一样。
至此完成与log4j的整合,其实与log4j整合也简单,就是在配置文件下面加一行引入log4j.properties文件的位置
wrapper.java.additional.1=-Dlog4j.configuration=file:../conf/log4j.properties
4.将tomcat注册为服务:设置tomcat服务开机启动,还是在没有JRE的环境下进行:(公司集成部署系统可以采用这种方式)
1.新建一目录,解压一个全新的tomcat,在%TOMCAT%目录下新建一个servicewrapper目录:
2.将自己的项目解压之后放在webapps目录下(这里采用目录解压部署,参考:
3.与上面部署一样将所需要的文件拷贝到servicewrapper目录下:
bin目录是启动服务的bat文件和wrapper.exe文件,conf是wrapper.conf文件,jre是拷贝的jre环境,lang是空目录,lib是wrapper.jar和wrapper.dll文件,logs用于存放wrapper.log文件,mylib可以存放自己以及第三方jar包(不过web项目用不到这个)。
4.修改wrapper.conf文件的配置:(重要)
#定义了一个根路径,注意set和.之间没有空格,下面就可以用%basePath%取此变量set.basePath=C:\Users\liqiang\Desktop\n2\tomcat\apache-tomcat-7.0.72#java.exe所在位置wrapper.java.command=%basePath%/servicewrapper/jre/bin/java.exe#日志级别wrapper.java.command.loglevel=INFO#依赖的包,第一个是wrapper包,第二个是自己打的包以及程序依赖包#jre的lb路径wrapper.java.classpath.1=%basePath%/servicewrapper/jre/lib/*.jar#wrapper.jar路径wrapper.java.classpath.2=%basePath%/servicewrapper/lib/wrapper.jar#自己的依赖jar包wrapper.java.classpath.3=%basePath%/servicewrapper/mylib/*.jar#tomcat依赖的包(tomcat启动类在这个包中)wrapper.java.classpath.4 = %basePath%/bin/bootstrap.jarwrapper.java.classpath.5 = %basePath%/bin/tomcat-juli.jar#Wrapper集成主类。有4种集成方式,适合tomcat这样启动使用一个类,#停止使用另一个类的应用的是WrapperStartStopApp类wrapper.java.mainclass=org.tanukisoftware.wrapper.WrapperStartStopApp#tomcat应用参数,无需修改(第一个参数是tomcat的启动类)wrapper.app.parameter.1=org.apache.catalina.startup.Bootstrapwrapper.app.parameter.2=1wrapper.app.parameter.3=startwrapper.app.parameter.4=org.apache.catalina.startup.Bootstrapwrapper.app.parameter.5=TRUEwrapper.app.parameter.6=1wrapper.app.parameter.7=stopwrapper.working.dir = %basePath%/bin#固定写法,依赖的wrapper的包wrapper.java.library.path.1=../lib#wrapper.log日志文件位置wrapper.logfile=%basePath%/servicewrapper/logs/wrapper.log#服务名称以及描述信息wrapper.console.title=Exam Serverwrapper.name=examservicewrapper.displayname=Exam Serverwrapper.description=Exam Server#Tomcat的固定参数(一般不用修改)wrapper.java.additional.1=-Djava.util.logging.config.file=%basePath%/conf/logging.propertieswrapper.java.additional.2 = -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager wrapper.java.additional.3 = -Djava.endorsed.dirs=%basePath%/endorsed -Dcatalina.base=%basePath% -Dcatalina.home=%basePath%wrapper.java.additional.4 = -Djava.io.tmpdir=%basePath%/tempwrapper.java.additional.5 = -Djava.net.preferIPv4Stack=truewrapper.java.additional.6 = -XX:MaxNewSize=256mwrapper.java.additional.7 = -XX:MaxPermSize=256mwrapper.java.additional.8 = -XX:-UseGCOverheadLimitwrapper.java.additional.9 = -Dcom.sun.management.jmxremotewrapper.jmx=falsewrapper.on_exit.0=SHUTDOWNwrapper.on_exit.default=RESTARTwrapper.ntservice.interactive = true#服务开机启动wrapper.ntservice.starttype=AUTO_STARTwrapper.tray = true#监测JVM死锁wrapper.java.monitor.deadlock = truewrapper.java.monitor.heap = truewrapper.java.monitor.gc.restart = true# Java Heap 初始化大小(单位:MB)wrapper.java.initmemory=1024# Java Heap 最大值(单位:MB)wrapper.java.maxmemory=1024
5.注册为服务:
C:\Users\liqiang\Desktop\n2\tomcat\apache-tomcat-7.0.72\servicewrapper>bin\wrapper.exe -i ..\conf\wrapper.confwrapperm | Exam Server service installed.C:\Users\liqiang\Desktop\n2\tomcat\apache-tomcat-7.0.72\servicewrapper>sc qc examservice[SC] QueryServiceConfig 成功SERVICE_NAME: examservice TYPE : 110 WIN32_OWN_PROCESS (interactive) START_TYPE : 2 AUTO_START ERROR_CONTROL : 1 NORMAL BINARY_PATH_NAME : C:\Users\liqiang\Desktop\n2\tomcat\apache-tomcat-7.0.72\servicewrapper\bin\wrapper.exe -s C:\Users\liqiang\Desktop\n2\tomcat\apache-tomcat-7.0.72\servicewrapper\conf\wrapper.conf wrapper.console.flush=true wrapper.internal.namedpipe=1139632464 LOAD_ORDER_GROUP : TAG : 0 DISPLAY_NAME : Exam Server DEPENDENCIES : SERVICE_START_NAME : LocalSystemC:\Users\liqiang\Desktop\n2\tomcat\apache-tomcat-7.0.72\servicewrapper>
6.启动服务并进行日志查看:
由于项目中使用了log4j,所以现在可以从两个地方查看log日志。第一个是log4j指定的日志的输出的位置,第二个是wrapper.logs文件。
7.监测Tomcat的参数信息:
可以jps+jmap进行查看(前提是安装JDK),如果没有安装JDK可以利用tomcat自带的manager项目进行监测(参考:)。如果需要对参数进行优化,只需要修改上面wrapper.conf文件即可。
总结:
上面还可以进行大量的优化,比如讲一些不用的文件删掉,将多个lib目录进行合并;
至此完成了tomcat注册为web服务,项目中一般使用这种方式进行部署项目,而且mysql可以采用集成部署集成到系统中,到时候系统上线的时候只用一个压缩包传到服务器,避免大量的下载tomcat、mysql等以及大量的配置。我上个项目上线也确实是通过安装JDK、mysql、tomcat等进行部署安装的,采用这种集成的方式都不用安装JDK等软件,简化部署。
5.linux环境下的注册为服务
参考:
附件:官网的一份配置:
#encoding=UTF-8# Configuration files must begin with a line specifying the encoding# of the the file.#********************************************************************# Wrapper License Properties (Ignored by Community Edition)#********************************************************************# Professional and Standard Editions of the Wrapper require a valid# License Key to start. Licenses can be purchased or a trial license# requested on the following pages:# http://wrapper.tanukisoftware.com/purchase# http://wrapper.tanukisoftware.com/trial# Include file problems can be debugged by removing the first '#'# from the following line:##include.debug# The Wrapper will look for either of the following optional files for a# valid License Key. License Key properties can optionally be included# directly in this configuration file.#include ../conf/wrapper-license.conf#include ../conf/wrapper-license-%WRAPPER_HOST_NAME%.conf# The following property will output information about which License Key(s)# are being found, and can aid in resolving any licensing problems.#wrapper.license.debug=TRUE#********************************************************************# Wrapper Localization#********************************************************************# Specify the locale which the Wrapper should use. By default the system# locale is used.#wrapper.lang=en_US # en_US or ja_JP# Specify the location of the Wrapper's language resources. If these are# missing, the Wrapper will default to the en_US locale.wrapper.lang.folder=../lang#********************************************************************# Wrapper Java Properties#********************************************************************# Java Application# Locate the java binary on the system PATH:wrapper.java.command=java# Specify a specific java binary:#set.JAVA_HOME=/java/path#wrapper.java.command=%JAVA_HOME%/bin/java# Tell the Wrapper to log the full generated Java command line.#wrapper.java.command.loglevel=INFO# Java Main class. This class must implement the WrapperListener interface# or guarantee that the WrapperManager class is initialized. Helper# classes are provided to do this for you. See the Integration section# of the documentation for details.wrapper.java.mainclass=org.tanukisoftware.wrapper.demo.DemoApp# Java Classpath (include wrapper.jar) Add class path elements as# needed starting from 1wrapper.java.classpath.1=../lib/wrapperdemo.jarwrapper.java.classpath.2=../lib/wrapper.jar# Java Library Path (location of Wrapper.DLL or libwrapper.so)wrapper.java.library.path.1=../lib# Java Bits. On applicable platforms, tells the JVM to run in 32 or 64-bit mode.wrapper.java.additional.auto_bits=TRUE# Java Additional Parameterswrapper.java.additional.1=# Initial Java Heap Size (in MB)#wrapper.java.initmemory=3# Maximum Java Heap Size (in MB)#wrapper.java.maxmemory=64# Application parameters. Add parameters as needed starting from 1wrapper.app.parameter.1=#********************************************************************# Wrapper Logging Properties#********************************************************************# Enables Debug output from the Wrapper.# wrapper.debug=TRUE# Format of output for the console. (See docs for formats)wrapper.console.format=PM# Log Level for console output. (See docs for log levels)wrapper.console.loglevel=INFO# Log file to use for wrapper output logging.wrapper.logfile=../logs/wrapper.log# Format of output for the log file. (See docs for formats)wrapper.logfile.format=LPTM# Log Level for log file output. (See docs for log levels)wrapper.logfile.loglevel=INFO# Maximum size that the log file will be allowed to grow to before# the log is rolled. Size is specified in bytes. The default value# of 0, disables log rolling. May abbreviate with the 'k' (kb) or# 'm' (mb) suffix. For example: 10m = 10 megabytes.wrapper.logfile.maxsize=0# Maximum number of rolled log files which will be allowed before old# files are deleted. The default value of 0 implies no limit.wrapper.logfile.maxfiles=0# Log Level for sys/event log output. (See docs for log levels)wrapper.syslog.loglevel=NONE#********************************************************************# Wrapper General Properties#********************************************************************# Allow for the use of non-contiguous numbered propertieswrapper.ignore_sequence_gaps=TRUE# Do not start if the pid file already exists.wrapper.pidfile.strict=TRUE# Title to use when running as a consolewrapper.console.title=Test Wrapper Sample Application#********************************************************************# Wrapper JVM Checks#********************************************************************# Detect DeadLocked Threads in the JVM. (Requires Standard Edition)wrapper.check.deadlock=TRUEwrapper.check.deadlock.interval=10wrapper.max_failed_invocations=99wrapper.console.fatal_to_stderr=FALSEwrapper.console.error_to_stderr=FALSEwrapper.check.deadlock.action=RESTARTwrapper.check.deadlock.output=FULL# Out Of Memory detection.# Ignore -verbose:class output to avoid false positives.wrapper.filter.trigger.1000=[Loaded java.lang.OutOfMemoryErrorwrapper.filter.action.1000=NONE# (Simple match)wrapper.filter.trigger.1001=java.lang.OutOfMemoryError# (Only match text in stack traces if -XX:+PrintClassHistogram is being used.)#wrapper.filter.trigger.1001=Exception in thread "*" java.lang.OutOfMemoryError#wrapper.filter.allow_wildcards.1001=TRUEwrapper.filter.action.1001=RESTARTwrapper.filter.message.1001=The JVM has run out of memory.#********************************************************************# Wrapper Email Notifications. (Requires Professional Edition)#********************************************************************# Common Event Email settings.#wrapper.event.default.email.debug=TRUE#wrapper.event.default.email.smtp.host=#wrapper.event.default.email.smtp.port=25#wrapper.event.default.email.subject=[%WRAPPER_HOSTNAME%:%WRAPPER_NAME%:%WRAPPER_EVENT_NAME%] Event Notification#wrapper.event.default.email.sender= #wrapper.event.default.email.recipient= # Configure the log attached to event emails.#wrapper.event.default.email.attach_log=TRUE#wrapper.event.default.email.maillog.lines=50#wrapper.event.default.email.maillog.format=LPTM#wrapper.event.default.email.maillog.loglevel=INFO# Enable specific event emails.#wrapper.event.wrapper_start.email=TRUE#wrapper.event.jvm_prelaunch.email=TRUE#wrapper.event.jvm_start.email=TRUE#wrapper.event.jvm_started.email=TRUE#wrapper.event.jvm_deadlock.email=TRUE#wrapper.event.jvm_stop.email=TRUE#wrapper.event.jvm_stopped.email=TRUE#wrapper.event.jvm_restart.email=TRUE#wrapper.event.jvm_failed_invocation.email=TRUE#wrapper.event.jvm_max_failed_invocations.email=TRUE#wrapper.event.jvm_kill.email=TRUE#wrapper.event.jvm_killed.email=TRUE#wrapper.event.jvm_unexpected_exit.email=TRUE#wrapper.event.wrapper_stop.email=TRUE# Specify custom mail contentwrapper.event.jvm_restart.email.body=The JVM was restarted.\n\nPlease check on its status.\n#********************************************************************# Wrapper Windows NT/2000/XP Service Properties#********************************************************************# WARNING - Do not modify any of these properties when an application# using this configuration file has been installed as a service.# Please uninstall the service before modifying this section. The# service can then be reinstalled.# Name of the servicewrapper.name=testwrapper# Display name of the servicewrapper.displayname=Test Wrapper Sample Application# Description of the servicewrapper.description=Test Wrapper Sample Application Description# Service dependencies. Add dependencies as needed starting from 1wrapper.ntservice.dependency.1=# Mode in which the service is installed. AUTO_START, DELAY_START or DEMAND_STARTwrapper.ntservice.starttype=AUTO_START# Allow the service to interact with the desktop.wrapper.ntservice.interactive=false
参考Java Service Wrapper官网介绍:
wrapper高级配置详解参考: