tags: java daemonhtml
上一篇已對使用java service wrapper工具與java程序集成進行了講解,java service wrapper使用簡單,集成方法簡單,不修改任何代碼,通常狀況下已知足需求。java
可是,java service wrapper只對java程序的開啓及關閉進行操做, 若須要對程序啓動前及關閉前進行一些自定義的操做(如啓動時初始化工做,關閉時釋放某些資源或進行特殊操做),此時就可使用apache commons daemon了。linux
Apache common deamon是用來提供java服務的安裝,實現將一個普通的 Java 應用變成系統的一個後臺服務,在linux下部署爲後臺運行程序,在windows部署爲windows服務(著名的tomcat就是使用它實現啓動及中止的。提供啓動、中止、卸載等操做)。詳細介紹可見commons-daemon官網。相對java service wrapper,commons daemon須要本身寫少量代碼,即按規定要求編寫程序啓動及關閉的入口類,僅此而已。git
本文主要講解兩部份內容:github
- linux下使用commons-daemon把java安裝爲後臺運行程序
- windows下使用commons-daemon把java安裝爲windows服務
本文示例程序與上一篇的示例(log4j+java service wrapper)放在同一工程(所以上一篇的示例本程序也適用),不過改成使用logback+commons-daemon。程序結構以下:
apache
- 主要DaemonMainClassForLinux、DaemonMainClassForWindows及LogbackFileLogger類及logback.xml,其它的(WrapperMainClassForWindows、FileLogger及log4j.properties)是屬於上一篇的示例。
- install目錄是用於對程序封裝爲服務的,即存放wrapper或daemon的,daemon目錄下有linux及windows的安裝內容
- src/main/assembly下存放打包成自定義格式的maven配置(分別是linux及windows)
- pom.xml配置打包插件信息
使用assembly打包,打包出windows包及linux包,包結構是bin,classes和lib三個目錄,分別是安裝目錄,java程序,依賴包,以下所示:
windows
linux下使用commons-daemon主要經過commons-daemon主程序及jsvc實現。本示例環境是centos6.5。centos
到commons-daemon官網下載,其中須要下載commons-daemon主程序和jsvc包(源碼包)。以下圖:
tomcat
可先查看官網的jsvc,本示例中安裝以下:把commons-daemon源碼包放到/opt目錄下。操做以下:bash
[root@localhost]# cd /opt/jsvc
[root@localhost jsvc]# unzip commons-daemon-1.0.15-src.zip
[root@localhost jsvc]# cd /opt/jsvc/commons-daemon-1.0.15-src/src/native/unix
[root@localhost unix]# ./configure --with-java=/usr/java/jdk1.8.0_51
*** All done ***
Now you can issue "make"
[root@localhost unix]# make
gcc jsvc-unix.o libservice.a -ldl -lpthread -o ../jsvc
make[1]: 離開目錄「/opt/jsvc/commons-daemon-1.0.15-src/src/native/unix/native」
複製代碼
說明
- 確保linux上已安裝java,安裝需依賴java,如上述操做中
--with-java=/usr/java/jdk1.8.0_51
- make完後,會在commons-daemon-1.0.15-src/src/native/unix下生成jsvc文件以下圖:
- 記住此jsvc路徑,安裝時使用。
程序實際的功能很簡單,以下(見LogbackFileLogger
類):
public class LogbackFileLogger {
private static Logger logger = LoggerFactory.getLogger(LogbackFileLogger.class);
public void logInfo2file() {
for (int i = 0; i < 50; i++) {
logger.info("個人測試:my test{}", i);
try {
//睡眠一秒以維持服務運行
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
logger.error(e.getMessage(),e);
}
}
}
}
複製代碼
入口類只須要實現init
,destroy
,start
,stop
方法便可,安裝時指定此類,jsvc會根據這些方法進行初始化、啓動、關閉等操做。程序以下:
/** * Linux服務安裝入口類 * * @author mason * */
public class DaemonMainClassForLinux {
public static void init(String[] args) {
//日誌輸出到程序根目錄(classpath)
MainClass.initWorkDir();
}
public static void destroy() {
}
public static void start() {
LogbackFileLogger logger = new LogbackFileLogger();
logger.logInfo2file();
}
public static void stop() {
System.exit(0);
}
}
複製代碼
說明:
- init方法有參數,destroy、start及stop方法無參。
- 本示例中的初始化
MainClass.initWorkDir();
用於指定日誌輸出的目錄。start
方法做啓動入口,stop
方法爲關閉,本示例簡單退出程序,若須要自定義操做,可在此方法編寫便可。
安裝腳本在示例install/daemon/linux下,主要設置程序名稱,路徑,jsvc路徑,java路徑,程序入口類,日誌輸出目錄便可。詳細以下:
#!/bin/sh
# description: jsw-test
# processname: jsw-test
# chkconfig: 234 20 80
# 程序名稱
SERVICE_NAME=jsw-test
#程序路徑獲取相對路徑,可填寫絕對路徑,如APP_HOME=/usr/local/bingo_cdp/deploy/bingo-cdp-timerjob-linux
APP_HOME=$(dirname $(pwd))
EXEC=/opt/jsvc/commons-daemon-1.0.15-src/src/native/unix/jsvc
JAVA_HOME=/usr/java/jdk1.8.0_51
#依賴路徑
cd ${APP_HOME}
CLASS_PATH="$PWD/classes":"$PWD/lib/*"
#程序入口類
CLASS=main.DaemonMainClassForLinux
#程序ID文件
PID=${APP_HOME}/${SERVICE_NAME}.pid
#日誌輸出路徑
LOG_OUT=${APP_HOME}/logs/${SERVICE_NAME}.out
LOG_ERR=${APP_HOME}/logs/${SERVICE_NAME}.err
#輸出
echo "service name: $SERVICE_NAME"
echo "app home: $APP_HOME"
echo "jsvc: $EXEC"
echo "java home: $JAVA_HOME"
echo "class path: $CLASS_PATH"
echo "main class: $CLASS"
#執行
do_exec()
{
$EXEC -home "$JAVA_HOME" -Dsun.jnu.encoding=UTF-8 -Dfile.encoding=UTF-8 -Djcifs.smb.client.dfs.disabled=false -Djcifs.resolveOrder=DNS -Xms512M -Xmx1024M -cp $CLASS_PATH -outfile $LOG_OUT -errfile $LOG_ERR -pidfile $PID $1 $CLASS
}
#根據參數執行
case "$1" in
start)
do_exec
echo "${SERVICE_NAME} started"
;;
stop)
do_exec "-stop"
echo "${SERVICE_NAME} stopped"
;;
restart)
if [ -f "$PID" ]; then
do_exec "-stop"
do_exec
echo "${SERVICE_NAME} restarted"
else
echo "service not running, will do nothing"
exit 1
fi
;;
status)
ps -ef | grep jsvc
;;
*)
echo "usage: service ${SERVICE_NAME} {start|stop|restart|status}" >&2
exit 3
;;
esac
複製代碼
通常還須要把程序做爲系統服務開機啓動,所以本示例中提供了set_auto_start.sh
文件,注意修改相應的文件名稱:
#!/bin/bash
DAEMON_HOME=$(pwd)
AUTO_RUN_FILE=${DAEMON_HOME}/jsw-test.sh
AUTO_RUN_FILE_NAME=jsw-test
echo "設置${AUTO_RUN_FILE_NAME}開機啓動......"
#若文件不存在,報錯、不然設置開機啓動
if [ ! -f "${AUTO_RUN_FILE}" ]; then
echo "${AUTO_RUN_FILE} 不存在,請檢查文件!"
else
\cp -f ${AUTO_RUN_FILE} /etc/init.d/${AUTO_RUN_FILE_NAME}
chmod 777 /etc/init.d/${AUTO_RUN_FILE_NAME}
chkconfig --add ${AUTO_RUN_FILE_NAME}
chkconfig ${AUTO_RUN_FILE_NAME} on
fi
echo "設置${AUTO_RUN_FILE_NAME}開機啓動結束"
複製代碼
使用maven的assembly插件打包,流程可見上一篇,當前示例打linux的zip包,具體配置以下: pom.xml
<execution>
<id>make-daemon-linux-zip</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<finalName>jsw-test</finalName>
<appendAssemblyId>true</appendAssemblyId>
<outputDirectory>${project.build.directory}</outputDirectory>
<descriptors>
<descriptor>src/main/assembly/daemon-linux-zip.xml</descriptor>
</descriptors>
</configuration>
</execution>
複製代碼
daemon-linux-zip.xml
<assembly>
<id>deamon-linux</id>
<formats>
<format>zip</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<dependencySets>
<dependencySet>
<useProjectArtifact>false</useProjectArtifact>
<outputDirectory>/lib</outputDirectory>
</dependencySet>
</dependencySets>
<fileSets>
<fileSet>
<!-- 源目錄,此處是把編譯出來的class文件都輸出到根目錄下的classes目錄-->
<directory>${project.build.directory}/classes</directory>
<outputDirectory>/classes</outputDirectory>
</fileSet>
<fileSet>
<directory>install/daemon</directory>
<includes>
<include>commons-daemon-1.0.15.jar</include>
</includes>
<outputDirectory>/lib</outputDirectory>
</fileSet>
<fileSet>
<!-- 此處是把daemon的腳本文件輸出到根目錄下的bin目錄-->
<directory>install/daemon/linux</directory>
<includes>
<include>jsw-test.sh</include>
<include>set_auto_start.sh</include>
</includes>
<outputDirectory>/bin</outputDirectory>
</fileSet>
</fileSets>
</assembly>
複製代碼
程序包(jsw-test-deamon-linux.zip)打出來後,便可放到linux下進行部署,部署過程以下:
[root@localhost opt]# unzip jsw-test-deamon-linux.zip -d jsw-test
[root@localhost opt]# cd jsw-test/bin
[root@localhost bin]# chmod 777 *.sh
[root@localhost bin]# ./jsw-test.sh start
複製代碼
說明:
- 本示例中放到/opt下,解壓,修改執行權限,執行。(
start
,stop
,restart
參數)。- 日誌文件按腳本設置的目錄,輸出在/opt/jsw-test/logs,其下查看啓動日誌:jsw-test.err jsw-test.out
- /opt/jsw-test/classes/logs下查看程序使用logback的輸出。
windows下使用commons-daemon安裝服務跟linux下流程上差很少,差異是使用procrun,腳本的編寫。
跟linux同樣,到commons-daemon官網下載。
commons-daemon-1.0.15-bin.zip
),解壓出commons-daemon-1.0.15.jar
,也可使用前面下載的jar包。commons-daemon-1.0.15-bin-windows.zip
。解壓出文件以下:commons-daemon-1.0.15.jar
,prunmgr.exe及prunsrv.exe到安裝目錄(見本示例中爲install及install/windows)。跟linux下的入口類不一樣,入口類只須要實現start
,stop
方法,安裝時指定此類,procrun會根據這些方法進啓動、關閉操做。注意:與linux的不一樣,此兩個方法須要帶參數,不然會報找不到start方法錯誤,程序以下:
/** * Windows服務安裝入口類 * * @author mason * */
public class DaemonMainClassForWindows {
public static void start(String[] args) {
// 日誌輸出到程序根目錄(classpath)
MainClass.initWorkDir();
LogbackFileLogger logger = new LogbackFileLogger();
logger.logInfo2file();
}
public static void stop(String[] args) {
System.exit(0);
}
}
複製代碼
使用procrun安裝成服務,需寫bat腳本進行安裝(見示例中的install/daemon/windows目錄下的install.bat,uninstall.bat)。主要是設置服務名稱,java路徑,依賴類,入口類,prunsrv路徑,日誌路徑。注意jvm的大小參數請根據實際狀況修改詳細以下: install.bat:
@echo off
rem 設置程序名稱
set SERVICE_EN_NAME=jsw-test
set SERVICE_CH_NAME=jsw測試程序
rem 設置java路徑
set JAVA_HOME=E:\Program Files\Java\jdk1.8.0_51
rem 設置程序依賴及程序入口類
cd..
set BASEDIR=%CD%
set CLASSPATH=%BASEDIR%\classes;%BASEDIR%\lib\*
set MAIN_CLASS=main.DaemonMainClassForWindows
rem 設置prunsrv路徑
set SRV=%BASEDIR%\bin\prunsrv.exe
rem 設置日誌路徑及日誌文件前綴
set LOGPATH=%BASEDIR%\logs
rem 輸出信息
echo SERVICE_NAME: %SERVICE_EN_NAME%
echo JAVA_HOME: %JAVA_HOME%
echo MAIN_CLASS: %MAIN_CLASS%
echo prunsrv path: %SRV%
rem 設置jvm
if "%JVM%" == "" goto findJvm
if exist "%JVM%" goto foundJvm
:findJvm
set "JVM=%JAVA_HOME%\jre\bin\server\jvm.dll"
if exist "%JVM%" goto foundJvm
echo can not find jvm.dll automatically,
echo please use COMMAND to localation it
echo for example : set "JVM=C:\Program Files\Java\jdk1.8.0_25\jre\bin\server\jvm.dll"
echo then install service
goto end
:foundJvm
rem 安裝
"%SRV%" //IS//%SERVICE_EN_NAME% --DisplayName="%SERVICE_CH_NAME%" "--Classpath=%CLASSPATH%" "--Install=%SRV%" "--JavaHome=%JAVA_HOME%" "--Jvm=%JVM%" --JvmMs=256 --JvmMx=1024 --Startup=auto --JvmOptions=-Djcifs.smb.client.dfs.disabled=false ++JvmOptions=-Djcifs.resolveOrder=DNS "--StartPath=%BASEDIR%" --StartMode=jvm --StartClass=%MAIN_CLASS% --StartMethod=start "--StopPath=%BASEDIR%" --StopMode=jvm --StopClass=%MAIN_CLASS% --StopMethod=stop --LogPath=%LOGPATH% --StdOutput=auto --StdError=auto
:end
複製代碼
uninstall.bat:
@echo off
cd..
set BASEDIR=%CD%
set SERVICE_NAME=jsw-test
set "SRV=%BASEDIR%\bin\prunsrv.exe"
%SRV% //DS//%SERVICE_NAME%
:end
複製代碼
通常安裝好服務後,則可在控制面板-管理程序-服務中進行啓動,關閉管理。若須要使用腳本啓動,可以使用prunmgr.exe進行管理,編寫以下:
@echo off
cd..
set BASEDIR=%CD%
set SERVICE_NAME=prunmgr
set MONITOR_PATH=%BASEDIR%\bin\prunmgr.exe
echo start %SERVICE_NAME%
%MONITOR_PATH% //MR//%SERVICE_NAME%
:end
複製代碼
使用maven的assembly插件打包,流程可見上一篇,當前示例打windows的zip包,具體配置以下: pom.xml
<execution>
<id>make-daemon-win-zip</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<finalName>jsw-test</finalName>
<appendAssemblyId>true</appendAssemblyId>
<outputDirectory>${project.build.directory}</outputDirectory>
<descriptors>
<descriptor>src/main/assembly/daemon-win-zip.xml</descriptor>
</descriptors>
</configuration>
</execution>
複製代碼
daemon-win-zip.xml
<assembly>
<id>daemon-win</id>
<formats>
<format>zip</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<dependencySets>
<dependencySet>
<useProjectArtifact>false</useProjectArtifact>
<outputDirectory>/lib</outputDirectory>
</dependencySet>
</dependencySets>
<fileSets>
<fileSet>
<!-- 源目錄,此處是把編譯出來的class文件都輸出到根目錄下的classes目錄-->
<directory>${project.build.directory}/classes</directory>
<outputDirectory>/classes</outputDirectory>
</fileSet>
<fileSet>
<directory>install/daemon</directory>
<includes>
<include>commons-daemon-1.0.15.jar</include>
</includes>
<outputDirectory>/lib</outputDirectory>
</fileSet>
<fileSet>
<!-- 此處是把daemon文件所有輸出到根目錄下的install目錄-->
<directory>install/daemon/windows</directory>
<outputDirectory>/bin</outputDirectory>
</fileSet>
</fileSets>
</assembly>
複製代碼
程序包(jsw-test-deamon-win.zip)打出來後,便可放到windows目錄下進行部署,部署過程以下:
jsw-test.zip; 密碼: uryd