分布式进阶(五)之JSVC配置

应用场景: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:chkconfigredhat公司遵循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 的系统运行级别:

系统停机状态

单用户或系统维护状态

2~5 多用户状态

重新启动

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的两个版本,CentOSUbuntu,需要在两个版本中都可以开机启动服务。Ubuntu没有 /etc/rc.d/init.d这个目录,所以,为了保持同一种服务在CentOSUbuntu使用的统一性,将服务脚本(注:服务脚本在两个不同版本中是不同的)都放在 /etc/init.d 目录下,最终达到的效果是相同的。

需要说明的是:在CentOSUbuntu两个版本中,除了服务脚本放置的目录是相同的,服务脚本的编写及服务配置都是不同的。比如CentOS使用Chkconfig进行配置,而Ubuntu使用sysv-rc-conf进行配置。

查看/etc/rc2.d/里面的apachemysql启动脚本,通常都是两个阿拉伯数字后再接一个英文字母,再加脚本名称,例如S20tomcat6.0.28。英文字母是S的都是会自动启动的,K则相反。所以只要找到apachemysql的启动脚本,把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服务就写好了,你可以在TestJsvcinit(),start(),stop(),destroy()方法里添加你的业务,做你想做的事。

 

下面讲解jsvc 启动Java Linux下的实现原理

 

jsvc 是在apachedaemon项目下的开源项目,主要功能可以使一些运行在普通用户下的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 到比较大的值。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。