分散式進階(五)之JSVC配置
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow
也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!
應用場景:在linux系統上進行專案開發,在部署java專案時,常用方法就是寫一個shell指令碼,但當伺服器重啟了,經常會忘了啟動shell指令碼了。所以我們需要把自己的應用變成linux的服務,當伺服器啟動的時候就自行啟動自己的應用。使用
在linux上以服務的方式啟動java程式步驟:
注:其實bin目錄下並沒有jsvc.tar.gz這個檔案,需要自己下載。解壓完之後呢,執行sh support/buildconf.sh 命令,提示autoconf:not found,如下圖所示:
若不存在jsvc.tar.gz包,還可以嘗試如下方法:
1、在/home/shq/apache-tomcat-6.0.28/bin中有commons-daemon-native.tar.gz 壓縮包
2、解壓commons-daemon-native.tar.gz
tar -zxvf commons-daemon-native.tar.gz
3、解壓完成,出現commons-daemon-1.0.2-native-src資料夾(目錄)
1、 進入commons-daemon-1.0.2-native-src 下的unix目錄
ls
cd commons-daemon-1.0.2-native-src/unix
2、 發現configure 配置檔案,執行如下修改屬性的命令
sudo chmod 777 configure
4、執行configure配置檔案,執行效果如下圖所示:
上面這個這個圖是因為沒有加後面的引數sudo ./configure --with -java=/usr/lib/java,新增後面的引數後,效果圖如下圖所示:
其實真正的命令應該是sudo ./configure --with -java=/usr/lib/java/jdk1.6.0_45也就是說後面的引數應該是jdk的所在目錄,因為裡面有相應的編譯工具。結果如下圖所示:
make時若出現以下錯誤,也可以通過上面的方法解決:
5、編譯
[[email protected] ~]# sudo make
執行結果如下圖所示:
#若報錯
[[email protected] ~]# make clean
[[email protected] ~]# make #重新編譯成功
6、編譯完成後出現jsvc資料夾,複製jsvc到/usr/lib/tomcat/apache-tomcat-6.0.28/bin目錄下面
[[email protected]localhost ~]# cp jsvc /usr/lib/tomcat/apache-tomcat-6.0.28/bin
7、在/etc/init.d/(注意不是init資料夾)目錄下編寫tomcat6啟動服務配置檔案。
[[email protected] ~]#cd /etc/init.d/
[[email protected] ~]# vi tomcat6.0.28
#!/bin/sh
# tomcat: Start/Stop/Restart tomcat
#
# chkconfig: 2345 85 15
# description: Apache tomcat6
# Small shell script to show how to start/stop Tomcat using jsvc
JAVA_HOME=/usr/lib/java/jdk1.6.0_45 #根據自己的實際jdk位置來修改
CATALINA_HOME=/usr/lib/tomcat//apache-tomcat-6.0.28 #根據自己實際tomcat位置來修改
DAEMON_HOME=$CATALINA_HOME/bin
# I did not use the user.
TOMCAT_USER=tomcat
# for multi instances adapt those lines.
TMP_DIR=$CATALINA_HOME/tmp
PID_FILE=$DAEMON_HOME/jsvc.pid
CATALINA_BASE=$CATALINA_HOME
. /etc/rc.d/init.d/functions
CATALINA_OPTS=
CLASSPATH=\
$JAVA_HOME/lib/tools.jar:\
$CATALINA_HOME/bin/commons-daemon.jar:\
$CATALINA_HOME/bin/bootstrap.jar
start() {
echo -n $"Starting Tomcat6.0.28: "
# If you want to specify a user to run Tomcat.
#increase the 'user $ TOMCAT_USER \' to the parameter list.
$DAEMON_HOME/jsvc \
-home $JAVA_HOME \
-Dcatalina.home=$CATALINA_HOME \
-Dcatalina.base=$CATALINA_BASE \
-Djava.io.tmpdir=$TMP_DIR \
-wait 10 \
-pidfile $PID_FILE \
-outfile $CATALINA_HOME/logs/catalina.out \
-errfile '&1' \
$CATALINA_OPTS \
-cp $CLASSPATH \
org.apache.catalina.startup.Bootstrap
echo "*****************************[ok]"
}
stop() {
echo -n $"Stopping Tomcat6: "
#
$DAEMON_HOME/jsvc \
-stop \
-pidfile $PID_FILE \
org.apache.catalina.startup.Bootstrap
echo "*****************************[ok]"
}
status() {
ps ax --width=1000 | grep "[o]rg.apache.catalina.startup.Bootstrap" | awk '{printf $1 " "}' | wc | awk '{print $2}' >/tmp/tomcat_process_count.txt
read line < /tmp/tomcat_process_count.txt
if [ $line -gt 0 ]; then
echo -n "tomcat6 ( pid "
ps ax --width=1000 | grep "[o]rg.apache.catalina.startup.Bootstrap" | awk '{printf $1 " "}'
echo ") is running "
else
echo "Tomcat6.0.28 is stopped"
fi
}
case "$1" in
start)
# Start Tomcat
start
exit $?
;;
stop)
# Stop Tomcat
stop
exit $?
;;
restart)
# Restart Tomcat
stop
sleep 3
start
exit $?
;;
status)
status
exit $?
;;
*)
echo "Usage: tomcat6 {start|stop|restart|status}"
exit 1;;
esac
#####
####
8、錄入完成,按Esc鍵。輸入: wq(vi下輸入:x)儲存退出並賦予/etc/init.d/tomcat6檔案可執行許可權
[[email protected]~]# chmod +x /etc/init.d/tomcat6.0.28
------------------新增服務-----------------------------------------
9、[[email protected]~] #chkconfig --add tomcat6.0.28 ##新增tomcat服務
若提示chkconfig命令未找到,則需要執行sudo apt-get install chkconfig
參考網址:http://blog.sina.com.cn/s/blog_66fb0c830100xm5y.html
PS:chkconfig是redhat公司遵循gpl規則所開發的程式在ubuntu上預設是不支援chkconfig命令的,但可以自己安裝。Ubuntu下是使用sysv-rc-conf來代替chkconfig。
sudo apt-get install sysv-rc-conf
安裝完成後,直接執行sudo sysv-rc-conf命令即可開啟該管理軟體,如下圖所示:
操作介面十分簡潔,你可以用滑鼠點選,也可以用鍵盤方向鍵定位,用空格鍵選擇, “X”表示開啟該服務。 用Ctrl+N翻下一頁,用Ctrl+P翻上一頁,用Q退出。
Ubuntu 的系統執行級別:
0 系統停機狀態
1 單使用者或系統維護狀態
2~5 多使用者狀態
6 重新啟動
Ubuntu下可以直接加入啟動程式,例如把 /etc/init/tomcat6.0.28 加入到系統自動 啟動列表中:
#sudo sysv-rc-conf tomcat6.0.28 on
想關閉tomcat6.0.28的開機自動啟動,只需#sudo mv /etc/init.d/tomcat6.0.28(注意將配置指令碼備份,否則刪除後就跪了。)就可以了。
參考網址:http://www.linuxidc.com/Linux/2012-10/73123p2.htm
10、[[email protected]~] #chkconfig –list |grep tomcat6.0.28 ##檢視tomcat服務是否被新增
--------------------啟動服務---------------------------------------------
11、[[email protected]~] # service tomcat6.0.28 start ##啟動tomcat服務
執行以上命令後,出現如下錯誤提示:
按照網上的說法,新增軟連線ln -s /lib/lsb/init-functions /etc/rc.d/init.d/functions,繼續提示錯誤,原因很明顯:軟連線的資料夾壓根就不存在!
繼續搜尋,發現以下說法:
在Linux中,/etc/init.d 和 /etc/rc.d/init.d這兩個目錄,都是用來放服務指令碼的,當Linux啟動時,會尋找這些目錄中的服務指令碼,並根據指令碼的run level確定不同的啟動級別。
在製作服務指令碼的過程中,使用了Linux的兩個版本,CentOS和Ubuntu,需要在兩個版本中都可以開機啟動服務。但Ubuntu沒有 /etc/rc.d/init.d這個目錄,所以,為了保持同一種服務在CentOS和Ubuntu使用的統一性,將服務指令碼(注:服務指令碼在兩個不同版本中是不同的)都放在 /etc/init.d 目錄下,最終達到的效果是相同的。
需要說明的是:在CentOS和Ubuntu兩個版本中,除了服務指令碼放置的目錄是相同的,服務指令碼的編寫及服務配置都是不同的。比如CentOS使用Chkconfig進行配置,而Ubuntu使用sysv-rc-conf進行配置。
檢視/etc/rc2.d/裡面的apache和mysql啟動指令碼,通常都是兩個阿拉伯數字後再接一個英文字母,再加指令碼名稱,例如S20tomcat6.0.28。英文字母是S的都是會自動啟動的,K則相反。所以只要找到apache和mysql的啟動指令碼,把S改成K就可以了。
以下命令實現開機啟動或不啟動某服務(真的太像chkconfig了)
sudo sysv-rc-conf 服務名 on(off)
12、[[email protected]~] # sudo sysv-rc-conf off ##停止tomcat服務
13、[[email protected]~] #sudo sysv-rc-conf --list tomcat6.0.28 ##檢視tomcat服務啟動狀態
命令執行效果如下:
Linux 系統主要啟動步驟:
1. 讀取MBR 的資訊,啟動Boot Manager Windows使用NTLDR作為Boot Manager,如果您的系統中安裝多個版本的 Windows,您就需要在NTLDR 中選擇您要進入的系統。Linux 通常使用功能強大,配置靈活的GRUB作為Boot Manager。
2. 載入系統核心,啟動init 程序.init 程序是Linux 的根程序,所有的系統程序都是它的子程序。
3. init 程序讀取 /etc/inittab 檔案中的資訊,並進入預設的執行級別,按順序執行該執行級別對應資料夾下的指令碼。指令碼通常以 start 引數啟動,並指向一個系統中的程式。
通常情況下, /etc/rcS.d/ 目錄下的啟動指令碼首先被執行,然後是/etc/rcN.d/ 目錄。例如您設定的執行級別為3,那麼它對應的啟動目錄為/etc/rc3.d/ 。
4. 根據 /etc/rcS.d/ 資料夾中對應的指令碼啟動Xwindow 伺服器xorgXwindow 為Linux 下的圖形使用者介面系統。
5. 啟動登入管理器,等待使用者登入Ubuntu 系統預設使用GDM 作為登入管理器,您在登入管理器介面中輸入使用者名稱和密碼後,便可以登入系統。(您可以在 /etc/rc3.d/資料夾中找到一個名為S13gdm 的連結)
2.編寫服務啟動類
package com.sohu.jsvc.test;
public class TestJsvc {
public static void main(String args[]) {
System.out.println("execute main method!");
}
public void init() throws Exception {
System.out.println("execute init method!");
}
public void init(String[] args) throws Exception{
System.out.println("execute init(args) method");
}
public void start() throws Exception {
System.out.println("execute start method!");
}
public void stop() throws Exception {
System.out.println("execute stop method!");
}
public void destroy() throws Exception{
System.out.println("execute destroy method!");
}
}
main方法可以去掉,但是init(String[] args),start(),stop(),destroy()方法不能少,服務在啟動時會先呼叫init(String[] args)方法,然後呼叫start()方法,在服務停止時會首先呼叫stop()方法,然後呼叫destroy() 方法.
3.把這個類打包成webservice_engine.jar放到/usr/lib目錄下
4.編寫啟動服務的指令碼 webservice_engineJSVC
#!/bin/sh
# myjsvc This shell script takes care of starting and stopping
#
# chkconfig: - 60 50
# description: tlstat stat is a stat data daemon.
# processname: webservice_engineJSVC
# Source function library.
. /etc/rc.d/init.d/functions
RETVAL=0
prog="webservice_engineJSVC"
# jdk的安裝目錄
JAVA_HOME=/usr/lib/java/jdk1.6.0_45
#應用程式的目錄
MYJSVC_HOME=/test
#jsvc所在的目錄
DAEMON_HOME=/usr/lib/tomcat//apache-tomcat-6.0.28/bin
#使用者
MYJSVC_USER=root
# for multi instances adapt those lines.
TMP_DIR=/var/tmp
PID_FILE=/var/run/tlstat.pid
#程式執行是所需的jar包,commons-daemon.jar是不能少的
CLASSPATH=/usr/lib/webservice_engine.jar:/usr/lib/tomcat/apache-tomcat-6.0.28/bin/commons-daemon.jar:
case "$1" in
start)
#
# Start TlStat Data Serivce
#
$DAEMON_HOME/jsvc -user $MYJSVC_USER -home $JAVA_HOME -Djava.io.tmpdir=$TMP_DIR -wait 10 -pidfile $PID_FILE #控制檯的輸出會寫到tlstat.out檔案裡
-outfile $MYJSVC_HOME/log/myjsvc.out -errfile '&1' -cp $CLASSPATH #服務啟動類
test.main.WebServiceEngine
#
# To get a verbose JVM
#-verbose # To get a debug of jsvc.
#-debug exit $?
;;
stop)
#
# Stop TlStat Data Serivce
#
$DAEMON_HOME/jsvc -stop -pidfile $PID_FILE test.main.WebServiceEngine
exit $?
;;
*)
echo "Usage Webjsvc start/stop"
exit 1;;
esac
5. 把myjsvc檔案拷貝到/etc/init.d/目錄下
6. #chmod -c +x /etc/init.d
7. 新增,啟動服務
#sudo sysv-rc-conf webservice_engineJSVC on
你可以從/test/log/myjsvc.out檔案裡看到如下資訊:
execute init(args) method
execute start method
#service myjsvc stop
你會發現/test/log/myjsvc.out檔案裡會增加如下資訊
execute stop method
execute destroy method
並且在系統重啟時會自動啟動myjsvc服務
好了,一個簡單的 liunx服務就寫好了,你可以在TestJsvc的init(),start(),stop(),destroy()方法裡新增你的業務,做你想做的事。
下面講解jsvc 啟動Java 在Linux下的實現原理。
jsvc 是在apache的daemon專案下的開源專案,主要功能可以使一些執行在普通使用者下的java程序獲取一些root許可權下的權利,比如埠在1024下等。
如何執行?
在自己的java程式碼中,實現start, init , stop,destroy的方法,將自己的編譯打成jar檔案, 通過呼叫jsvc 來啟動
./jsvc -java-home /usr/java/jdk1.7.0_09/ -user nobody -pidfile /opt/apache-tomcat_1/logs/catalina-daemon1.pid -wait 10 -errfile "/tmp/error" -outfile "/tmp/output" -debug -classpath /root/test.jar:/root/commons-daemon.jar test
幾個注意點:
commons_daemon.jar檔案是用於呼叫你的class檔案,
關於-java-home, 在這裡有一個bug(https://issues.apache.org/jira/browse/DAEMON-268),哪怕指定,也會指定為預設的/usr/java
實現原理?
jsvc 是一個原始碼是c的程式,通過fork出子程序去啟動java,而程序成為控制程序,可以實現監視java子程序的目地。
改變啟動虛擬機器的程序使用者id和使用者組
通過呼叫setgid,setuid來改變當前程序的使用者id和組,這裡要注意的是當改變使用者id和組的時候,當前程序會改變程序的capability, 所以需要reset 程序的capability。
檢視程序的capability可以通過核心呼叫 __NR_capget / __NR_capset 的方式
static int get_legacy_caps(){
struct __user_cap_header_struct caphead;
struct __user_cap_data_struct cap;
memset(&caphead, 0, sizeof caphead);
caphead.version = LEGACY_CAP_VERSION;
if (syscall(__NR_capget, &caphead, &cap) < 0)
log_error("capget failed: %m");
log_debug("PID is %d print the cap 0x%x, 0x%x, 0x%x\n", getpid(), cap.effective, cap.permitted, cap.inheritable);
return 0;
}
啟動java
通過呼叫JNI_CreateJavaVM 啟動虛擬機器器,同時呼叫包common-daemon裡的DaemonLoader class, 呼叫你所寫的類中的start,...這些方法。
碰到的問題
在jsvc 裡啟動java以後就將jvm的虛擬機器的程序的capability 設定成了0, 導致在虛擬機器裡的建立執行緒受到max process 的控制, ulimit -u
已經建立issue: https://issues.apache.org/jira/browse/DAEMON-270, 短期解決辦法可以設定ulimit 到比較大的值。