1. 程式人生 > >Java Service Wrapper 將Java程式部署成系統服務

Java Service Wrapper 將Java程式部署成系統服務

在使用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目錄結構

解壓後目錄如下圖:
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的配置檔案中正確配置依賴檔案,如下:
Java程式打包成可執行Jar包

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[email protected].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簡介與使用