应用场景:在linux系统上进行项目开发,在部署java项目时,常用方法就是写一个shell脚本,但当服务器重启了,经常会忘了启动shell脚本了。所以我们需要把自己的应用变成linux的服务,当服务器启动的时候就自行启动自己的应用。使用JSVC就能够实现上面的功能。
Jsvc是用来启动tomcat的,在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、编译
[root@localhost ~]# sudo make
运行结果如下图所示:
#若报错
[root@localhost ~]# make clean
[root@localhost ~]# make #重新编译成功
6、编译完成后出现jsvc文件夹,复制jsvc到/home/shq/apache-tomcat-6.0.28/bin目录下面
[root@localhost ~]# cp jsvc /home/shq/apache-tomcat-6.0.28/bin
7、在/etc/init.d/(注意不是init文件夹)目录下编写tomcat6启动服务配置文件。
[root@localhost ~]#cd /etc/init.d/
[root@localhost ~]# 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=/home/shq/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 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文件可执行权限
[root@localhost~]# chmod 777 /etc/init.d/tomcat6.0.28
------------------添加服务-----------------------------------------
9、[root@localhost~] #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、[root@localhost~] #chkconfig –list |grep tomcat6.0.28 ##查看tomcat服务是否被添加
--------------------启动服务---------------------------------------------
11、[root@localhost~] # 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、[root@localhost~] # sudo sysv-rc-conf off ##停止tomcat服务
13、[root@localhost~] #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.把这个类打包成testjsvc.jar 放到/test目录下
4.编写启动服务的脚本 myjsvc
#!/bin/sh
# myjsvc This shell script takes care of starting and stopping
#
# chkconfig: - 60 50
# description: tlstat stat is a stat data daemon.
# processname: myjsvc
# Source function library.
. /etc/rc.d/init.d/functions
RETVAL=0
prog="MYJSVC"
# jdk的安装目录
JAVA_HOME=/usr/lib/java/jdk1.6.0_45
#应用程序的目录
MYJSVC_HOME=/test
#jsvc所在的目录
DAEMON_HOME=/home/shq/apache-tomcat-6.0.28/bin/jsvc-src
#用户
MYJSVC_USER=root
# for multi instances adapt those lines.
TMP_DIR=/var/tmp
PID_FILE=/var/run/tlstat.pid
#程序运行是所需的jar包,commons-daemon.jar是不能少的
CLASSPATH=/test/testjsvc.jar:/usr/local/tomcat5/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 #服务启动类
com.sohu.jsvc.test.TestJsvc
#
# 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 com.sohu.jsvc.test.TestJsvc
exit $?
;;
*)
echo "Usage myjsvc start/stop"
exit 1;;
esac
5. 把myjsvc文件拷贝到/etc/init.d/目录下
6. #chmod -c 777 /etc/init.d/myjsvc
7. 添加服务
#chkconfig --add myjsvc
#chkconfig --level 345 myjsvc on
8. 完成,启动服务
#service myjsvc start
你可以从/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 到比较大的值。
原文地址:http://blog.csdn.net/sunhuaqiang1/article/details/45874497