在使用Java开发应用程序时,我们通常会打包成Jar包独立运行,但是在实际生产环境中,服务器重启或出现异常时,程序往往不能重启或随服务器开机启动,所以我们可以采用Java Service Wrapper工具解决这一问题,Wrapper可以将我们的Java程序包装成系统服务,这样就可以随着系统的运行而自动运行了。本文主要介绍使用Wrapper将Java程序装成系统服务的方法,以linux系统为例。
1. 下载Wrapper
Wrapper下载地址:http://wrapper.tanukisoftware.com/doc/english/download.jsp
Wrapper几乎支持所有的系统环境,目前最新版本为3.5.33,下载Linux x86 64bit版本,作者使用的是3.5.32版。
2. Java Service Wrapper目录结构
解压后目录如下图:
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 //原始配置
3. 包装Java 应用程序
这里我们将Wrapper和StreamServer程序进行集成,将程序打包成可运行的Jar包之后按一下步骤操作,在打包时一定要选择将程序的依赖包以子文件夹的形式打包,这样才可以在wrapper的配置文件中正确配置依赖文件,如下:
3.1 建立目录结构
StreamServer
├─bin
├─conf
├─lib
├─logs
└─src
复制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目录下。
3.2 编辑testwrapper文件
该文件是应用程序的启动入口,将文件重命名为程序名
#mv testwrapper StreamServer
修改文件内容
# Application
APP_NAME="StreamServer"
APP_LONG_NAME="StreamServer Application"
# Wrapper
WRAPPER_CMD="./wrapper"
WRAPPER_CONF="../conf/wrapper.conf"
3.3 编辑wrapper.conf.in 文件
重命名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.
3.4 完成
修改成功后的文件夹目录结构如下:
├─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
4. 部署成系统服务
将上面的StreamServer文件夹移动到linux服务器/opt目录下
4.1 赋予服务执行权限
chmod 775 /opt/StreamServer/bin/StreamServer
chmod 775 /opt/StreamServer/bin/wrapper
4.2 让服务Server开机自动运行
ln -s /opt/StreamServer/bin/StreamServer /etc/init.d/StreamServer
#添加系统服务,此时服务会被在/etc/rc.d/rcN.d中赋予K/S入口了
chkconfig --add StreamServer
4.3 测试
执行命令: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简介与使用