wrapper
wrapper 启动程序说明
启动模式
启动wrapper时,会同时启动两个进程。
一个主进程:不“干实事”,只做为守护进程;
一个子进程:“干实事”的,生死由主进程掌握,主进程会定时检测子进程的状态,包括:死锁、内存溢出、响应(类似心跳),一旦发现异常,主进程会重启子进程(可以配置)。
启动命令
- Dubbo服务
bin/service.sh start
- Web服务 (tomcat与wrapper整合后的版本)
bin/tomcat.sh start
不要用TOMCAT自带的startup.sh与shutdown.sh来启停程序
Usage: ./tomcat.sh or ./service.sh [ console | start | stop | restart | condrestart | status | install | remove | dump ]
Commands:
console Launch in the current console.
start Start in the background as a daemon process.
stop Stop if running as a daemon or in another console.
restart Stop if running and then start.
condrestart Restart only if already running.
status Query the current status.
install Install to start automatically when system boots.
remove Uninstall.
dump Request a Java thread dump if running.
查看主进程PID
方法1:直接查看PID文件
* Dubbo服务
ROOT/bin/projectname.pid
* Web服务
apache-tomcat/bin/tomcat.pid
方法2:ps查询程序目录名称
ps -ef|grep 程序目录名
方法3:根据业务端口号查询
步骤1:先查询到子进程PID.
childPID=lsof -i:26001 |grep LISTEN|grep -v COMMAND|awk '{print $2}'
步骤2:根据子进程号查询主进程PID
ps -ef|grep 18856|grep -v grep|awk ‘{print $3}’
停止程序
方法1:
* Dubbo服务
bin/service.sh stop
* Web服务
bin/tomcat.sh stop
方法2:
kill -9 主进程PID
wrapper自动重启问题定位
重启原因
主进程会定时检测子进程的状态,包括:死锁、内存溢出、响应(类似心跳),一旦发现异常,主进程会重启子进程。
查看wrapper.log日志文件
# 查看重启日志
cat wrapper.log |grep -B 100 -A 50 "Restarting JVM"
# 查看错误日志
cat wrapper.log |grep -B 100 -A 50 "ERROR"
# 查看内存溢出日志
cat wrapper.log |grep "OutOfMemoryError"
碰到的问题汇总
- 问题一:【gigold-web-dispatchingCenter】总是在夜晚自动重启,导致调度任务停止
wrapper.log中出现如下日志:
STATUS | wrapper | 2016/03/17 03:44:55 | Pinging the JVM took 14 seconds to respond.
STATUS | wrapper | 2016/03/17 03:44:55 | Pinging the JVM took 10 seconds to respond.
STATUS | wrapper | 2016/03/17 03:45:51 | JVM appears hung: Timed out waiting for signal from JVM. Restarting JVM.
……
INFO | jvm 1 | 2016/03/17 03:45:59 | 17-Mar-2016 03:45:59.803 INFO [WrapperListener_stop_runner] org.apache.catalina.core.StandardService.stopInternal Stopping service Catalina
ERROR | wrapper | 2016/03/17 03:46:26 | Shutdown failed: Timed out waiting for signal from JVM.
ERROR | wrapper | 2016/03/17 03:46:26 | JVM did not exit on request, termination requested.
STATUS | wrapper | 2016/03/17 03:46:26 | JVM received a signal SIGKILL (9).
STATUS | wrapper | 2016/03/17 03:46:26 | JVM process is gone.
STATUS | wrapper | 2016/03/17 03:46:26 | JVM exited after being requested to terminate.
STATUS | wrapper | 2016/03/17 03:46:30 | Launching a JVM...
INFO | jvm 2 | 2016/03/17 03:46:31 | WrapperManager: Initializing...
原因:
部署此程序的机器为单核,且有的定时任务运行时长超过30秒,导致CPU长时间过载无法及时响应主进程状态检测,主进程在检测子进程状态时,如果子进程超过30秒没有响应,则主进程认为子进程异常,会重启子进程,重启超时时会直接kill掉子进程再重启启动。
解决方法:
配置文件中添加配置荐,增大超时时间
# 注意:此检测类似与心跳,除此项检测外,还有【死锁】与【内存溢出】的检测
# 子进程响应超时时间,单位:秒
wrapper.ping.timeout=30(default)
# 子进程检测子进程间隔时间
wrapper.ping.interval = 5 (default)
参考资料1:
参考资料2:
wrapper自身的内存溢出bug导致进程假死
现象:wrapper进程占用大量内存,应用进程无响应、且无法启动,只能重启wrapper进程来恢复
影响版本:3.5.27及更低版本
问题原因:根源是glibc的一个bug,wrapper在3.5.28之后绕过了此bug
对此bug的技术分析参见:https://sourceforge.net/p/wrapper/mailman/message/33043391/
解决方案:升级wrapper至3.5.28,或升级glibc至2.21
wrapper官方的修订记录,引自:http://wrapper.tanukisoftware.com/doc/english/release-notes.html
Version 3.5.28: Changes and bug fixes
Fix a memory leak on Red Hat Enterprise Linux, CentOS and Amazon Linux AMI. It occured when activating log rolling in size mode. This memory leak came from an issue in the GNU C Library (glibc) which was fixed on version 2.21. However, a workaround was added so that the Wrapper can run without issues on any version of glibc.
wrapper配置文件详解
参考资料1
#文件编码,每个配置文件起始位置必须指定该文件的编码格式
encoding=UTF-8
# 如果包含配置文件出现问题可以使用debug调试模式,去掉一个"#",格式为#include.debug
#include.debug
# 包含子配置文件,可以是配置信息也可以是许可信息
include ../conf/wrapper-license.conf
include ../conf/wrapper2.conf
# 是否开启许可文件debug模式
wrapper.license.debug=TRUE
# 指定Wrapper语言,默认使用系统语言
wrapper.lang=en_US
#指定Wrapper 语言资源位置,如果该文件不存在则默认设置为en_US
wrapper.lang.folder=../lang
# 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
# Java Main class,也就是程序入口
#该类需要实现WrapperListener 接口并保证WrapperManager 得到初始化(调用WrapperManager.start(WrapperListener listener, String[] args) 方法)。
wrapper.java.mainclass=com.helloworld.hello.HelloWorld
# Java Classpath配置,必须从序号"1"开始,添加新的jar包后序号递增
wrapper.java.classpath.1=../lib/wrapper.jar
wrapper.java.classpath.2=../lib/hello.jar
# Java 类库路径 (Wrapper.DLL 或 libwrapper.so 依赖文件的存放位置)
wrapper.java.library.path.1=../lib
# 32/64位选择,true为自动选择
wrapper.java.additional.auto_bits=TRUE
# Java附加参数
wrapper.java.additional.1=
# Java Heap 初始化大小(单位:MB)
wrapper.java.initmemory=3
# Java Heap 最大值(单位:MB)
wrapper.java.maxmemory=64
# 应用程序参数,也就是main函数的String[] args参数值,序号需从"1"开始,例如:
wrapper.app.parameter.1=g21121
wrapper.app.parameter.2=http://286.iteye.com/
# 是否显示debug日志
wrapper.debug=TRUE
# 控制台信息输出格式
wrapper.console.format=PM
# 控制台日志级别
wrapper.console.loglevel=INFO
# 日志文件位置及名称
wrapper.logfile=../logs/wrapper.log
# 日志文件输出格式
wrapper.logfile.format=LPTM
# 日志文件日志级别
wrapper.logfile.loglevel=INFO
# 限制日志文件大小,0为不限制,参数:k,m,g等
wrapper.logfile.maxsize=10m
# 限制最大日志文件数,0为不限制
wrapper.logfile.maxfiles=0
# syslog 日志级别
wrapper.syslog.loglevel=NONE
# 允许使用非连续编号的属性,例如:path的序号可以打乱
wrapper.ignore_sequence_gaps=TRUE
# 如果pid文件已经存在则不启动程序
wrapper.pidfile.strict=TRUE
# 控制台启动时显示的标题
wrapper.console.title=------------Wrapper Console------------
# 检测JVM中的死锁线程(需要标准版Wrapper)
wrapper.check.deadlock=TRUE
#间隔,单位:秒
wrapper.check.deadlock.interval=10
#出现死锁时处理事件
wrapper.check.deadlock.action=RESTART
#信息输出级别,FULL:全部;SIMPLE:精简;NONE:无;
wrapper.check.deadlock.output=FULL
# 内存溢出检测,Wrapper提供了几种不同的匹配机制
wrapper.filter.trigger.999=wrapper.filter.trigger.*java.lang.OutOfMemoryError
wrapper.filter.allow_wildcards.999=TRUE
wrapper.filter.action.999=NONE
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.
# 邮件基本信息设置
wrapper.event.default.email.debug=TRUE
#smtp服务器地址
wrapper.event.default.email.smtp.host=
#smtp服务器端口
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=<Sender email>
#收件人地址
wrapper.event.default.email.recipient=<Recipient email>
# 指定文件内容
wrapper.event.jvm_restart.email.body=The JVM was restarted.\n\nPlease check on its status.\n
# 邮件日志相关配置
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
# 触发事件,即当以下事件为true时发送邮件
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