在使用Java开发应用程序时,我们通常会打包成Jar包独立运行,但是在实际生产环境中,服务器重启或出现异常时,程序往往不能重启或随服务器开机启动,所以我们可以采用Java Service Wrapper工具解决这一问题,Wrapper可以将我们的Java程序包装成系统服务,这样就可以随着系统的运行而自动运行了。本文主要介绍使用Wrapper将Java程序装成系统服务的方法,以linux系统为例。
Wrapper下载地址:http://wrapper.tanukisoftware.com/doc/english/download.jsp Wrapper几乎支持所有的系统环境,目前最新版本为3.5.33,下载Linux x86 64bit版本,作者使用的是3.5.32版。
解压后目录如下图:
cmd 控制台下输入 tree /f 可以查看wrapper的详细目录和文件结构,显示目录结构如下:
│ README_de.txt //说明 │ README_en.txt //说明 │ README_es.txt //说明 │ README_ja.txt //说明 │ ├─bin //执行文件目录 │ demoapp //示例程序 │ testwrapper //测试程序 │ wrapper //主程序(重要) │ ├─conf //配置文件目录 │ demoapp.conf //示例配置文件 │ wrapper.conf //主配置文件(重要,文件名可改) │ ├─doc //说明文档目录 │ index.html //首页 │ revisions.txt //版本说明 │ wrapper-community-license-1.3.txt //许可协议 │ ├─lib //依赖类库目录 │ libwrapper.so //wrapper linux文件(.so:用户层的动态库) │ wrapper.jar //wrapper主程序(重要) │ wrapperdemo.jar //示例程序 │ wrappertest.jar //测试程序 │ ├─logs //日志目录 │ wrapper.log //日志文件 │ └─src //源代码目录 ├─bin //执行程序目录 │ sh.script.in //shell脚本源代码(重要) │ └─conf //配置目录 wrapper.conf.in //原始配置这里我们将Wrapper和StreamServer程序进行集成,将程序打包成可运行的Jar包之后按一下步骤操作,在打包时一定要选择将程序的依赖包以子文件夹的形式打包,这样才可以在wrapper的配置文件中正确配置依赖文件,如下:
复制Java Service Wrapper文件到应用程序目录 (1)复制/bin/wrapper到应用的bin目录; (2)复制/bin/testwrapper到应用的bin目录; (5)复制/src/conf/wrapper.conf.in到应用的conf目录下; (6)复制/lib/libwrapper.so到应用的lib目录; (7)复制/lib/wrapper.jar到应用的lib目录; 复制自己的程序Jar包和依赖文件夹到src目录下; 注:由于本程序使用了自定义的配置文件config.properties和日志配置文件log4j.properties,而程序中配置文件的引用路径是/config/config.properties,所以需要将配置文件config.properties和log4j.properties单独拷贝出来,复制到src/config目录下。
该文件是应用程序的启动入口,将文件重命名为程序名 #mv testwrapper StreamServer 修改文件内容
# Application APP_NAME="StreamServer" APP_LONG_NAME="StreamServer Application" # Wrapper WRAPPER_CMD="./wrapper" WRAPPER_CONF="../conf/wrapper.conf"重命名wrapper.conf.in为wrapper.conf,所有java service wrapper配置项均在此设置,按以下步骤进行设置:
文件编码及子配置文件文件头部包含了配置文件编码格式,子配置文件等相关信息,如下所示:
#文件编码,每个配置文件起始位置必须指定该文件的编码格式 encoding=UTF-8 # 如果包含配置文件出现问题可以使用debug调试模式,去掉一个"#" #include.debug # 包含子配置文件,可以是配置信息也可以是许可信息 include ../conf/wrapper-license.conf include ../conf/wrapper2.conf # 是否开启许可文件debug模式 wrapper.license.debug=TRUE Wrapper 语言设置通过这两项的设置可以指定Wrapper 的语言种类,可以在Wrapper 官网下到这些语言包支持,目前不支持中文。
# 指定Wrapper语言,默认使用系统语言 wrapper.lang=en_US #指定Wrapper 语言资源位置,如果该文件不存在则默认设置为en_US wrapper.lang.folder=../lang java运行环境设置 本程序使用PATH环境变量配置信息 # Java 程序配置: # (1)默认使用PATH环境变量配置信息则使用下列配置形式 wrapper.java.command=java # (2)如果想单独配置运行程序,则可采用此种配置方式 #set.JAVA_HOME=/java/path #wrapper.java.command=%JAVA_HOME%/bin/java # java程序日志级别 wrapper.java.command.loglevel=INFO 程序入口 wrapper的主类,可以是org.tanukisoftware.wrapper.WrapperStartStopApp或WrapperSimpleApp WrapperStartStopApp可以设置应用程序的启动和关闭入口 # Java Main class,也就是程序入口 #该类需要实现WrapperListener 接口并保证WrapperManager 得到初始化 wrapper.java.mainclass=wrapper.java.mainclass=org.tanukisoftware.wrapper.WrapperStartStopApp 类库设置 # Java Classpath配置,必须从序号"1"开始,添加新的jar包后序号递增 wrapper.java.classpath.1=../lib/wrapper.jar wrapper.java.classpath.2=../src/StreamServer.jar wrapper.java.classpath.3=../src/StreamServer_lib/netty-all-4.0.45.Final.jar wrapper.java.classpath.4=../src/StreamServer_lib/log4j-1.2.17.jar ... # Java 类库路径 (Wrapper.DLL 或 libwrapper.so 依赖文件的存放位置) wrapper.java.library.path.1=../lib JVM相关配置 # 32/64位选择,true为自动选择 wrapper.java.additional.auto_bits=TRUE # Java附加参数,若有log4j配置文件,则要添加参数 wrapper.java.additional.1=-Dlog4j.configuration=../src/config/log4j.properties附加参数即为java命令可选参数,如下所示:
-d32 use a 32-bit data model if available -d64 use a 64-bit data model if available -server to select the "server" VM The default VM is server. -cp <class search path of directories and zip/jar files> -classpath <class search path of directories and zip/jar files> A : separated list of directories, JAR archives, and ZIP archives to search for class files. -D<name>=<value> set a system property -verbose[:class|gc|jni] enable verbose output -version print product version and exit -version:<value> require the specified version to run -showversion print product version and continue -jre-restrict-search | -jre-no-restrict-search include/exclude user private JREs in the version search -? -help print this help message -X print help on non-standard options -ea[:<packagename>...|:<classname>] -enableassertions[:<packagename>...|:<classname>] enable assertions -da[:<packagename>...|:<classname>] -disableassertions[:<packagename>...|:<classname>] disable assertions -esa | -enablesystemassertions enable system assertions -dsa | -disablesystemassertions disable system assertions -agentlib:<libname>[=<options>] load native agent library <libname>, e.g. -agentlib:hprof see also, -agentlib:jdwp=help and -agentlib:hprof=help -agentpath:<pathname>[=<options>] load native agent library by full pathname -javaagent:<jarpath>[=<options>] load Java programming language agent, see java.lang.instrument -splash:<imagepath> show splash screen with specified image内存大小设置:
# Java Heap 初始化大小(单位:MB) wrapper.java.initmemory=3 # Java Heap 最大值(单位:MB) wrapper.java.maxmemory=64 应用程序参数设置:WrapperStartStopApp的配置形式
# 应用程序参数,序号需从"1"开始 # 程序的主函数所在类入口 wrapper.app.parameter.1=com.stream.server.StreamServer wrapper.app.parameter.2=1 wrapper.app.parameter.3=true # 程序的关闭函数所在类入口 wrapper.app.parameter.4=com.stream.server.StreamServer wrapper.app.parameter.5=true wrapper.app.parameter.6=1 # 程序的关闭函数 wrapper.app.parameter.7=stop Wrapper 日志配置 # 是否显示debug日志 #wrapper.debug=TRUE # 控制台信息输出格式(格式查看docs文档) wrapper.console.format=PM # 控制台日志级别 wrapper.console.loglevel=INFO # 日志文件位置及名称 wrapper.logfile=../logs/wrapper.log # 日志文件输出格式 (格式查看docs文档) wrapper.logfile.format=LPTM # 日志文件日志级别 wrapper.logfile.loglevel=INFO # 限制日志文件大小,0为不限制,参数:k,m,g等 wrapper.logfile.maxsize=50m # 限制最大日志文件数,0为不限制 wrapper.logfile.maxfiles=0 # syslog 日志级别 wrapper.syslog.loglevel=NONE Wrapper 基本属性配置 # 允许使用非连续编号的属性,例如:path的序号可以打乱 wrapper.ignore_sequence_gaps=TRUE # 如果pid文件已经存在则不启动程序 wrapper.pidfile.strict=TRUE # 控制台启动时显示的标题 wrapper.console.title=@app.long.name@ Wrapper JVM 检查 # 检测JVM中的死锁线程(需要标准版Wrapper) wrapper.check.deadlock=TRUE #间隔,单位:秒 wrapper.check.deadlock.interval=60 #出现死锁时处理事件 wrapper.check.deadlock.action=RESTART #信息输出级别,FULL:全部;SIMPLE:精简;NONE:无; wrapper.check.deadlock.output=FULL内存溢出检测
# 内存溢出检测,Wrapper提供了几种不同的匹配机制 wrapper.filter.trigger.1000=[Loaded java.lang.OutOfMemoryError wrapper.filter.action.1000=NONE wrapper.filter.trigger.1001=java.lang.OutOfMemoryError #wrapper.filter.trigger.1001=Exception in thread "*" java.lang.OutOfMemoryError #wrapper.filter.allow_wildcards.1001=TRUE wrapper.filter.action.1001=RESTART wrapper.filter.message.1001=The JVM has run out of memory.修改成功后的文件夹目录结构如下:
├─bin │ StreamServer │ wrapper │ ├─conf │ wrapper.conf │ ├─lib │ libwrapper.so │ wrapper.jar │ ├─logs └─src │ StreamServer.jar │ ├─config │ config.properties │ log4j.properties │ └─StreamServer_lib java_websocket.jar log4j-1.2.17.jar log4j-api-2.0-rc1.jar log4j-core-2.0-rc1.jar netty-all-4.0.45.Final.jar slf4j-api-1.7.5.jar slf4j-log4j12-1.7.5.jar将上面的StreamServer文件夹移动到linux服务器/opt目录下
执行命令:service StreamServer start|stop|restart|status 程序运行时,Java Service Wrapper在/opt/StreamServer/logs/目录下产生wrapper.log日志
注意: 1)若StreamServer 需要访问mysql数据库,则要查看开机启动顺序是否在mydqld服务之前,若在之前应进行如下操作 删除服务添加之后/etc/rc2.d,rc3.d,rc4.d,rc5.d中的StreamServer服务启动命令文件,将该命令文件的启动顺序改到mysqld服务之后,S+数字 该数字在mysqld服务之后 删除地址软链接使用 rm -rf symbolic_name ln -s /etc/init.d/StreamServer /etc/rc3.d/S90StreamServer ln -s /etc/init.d/StreamServer /etc/rc5.d/S90StreamServer
2)执行出现错误 /bin/sh^M: bad interpreter:没有那个文件或目录解决 错误分析: 因为我在windows下编辑的脚本,所以有可能有不可见字符。 脚本文件是DOS格式的, 即每一行的行尾以\n\r来标识, 其ASCII码分别是0x0D, 0x0A. 可以有很多种办法看这个文件是DOS格式的还是UNIX格式的, 还是MAC格式的
解决方法是使用dos2unix命令转一下,即输入: dos2unix 文件名 dos2unix /opt/SocketServer/bin/StreamServer dos2unix /opt/SocketServer/bin/wrapper
参考: Java Service Wrapper使用总结 Wrapper配置详解及高级应用 Java Service Wrapper简介与使用
