JSVC简介及原理

1,604 阅读2分钟

简介

jsvc可以理解为类unix系统下的启动并守护java进程的可执行程序,属于Apache Commons Daemon项目。

使用方法

编译jsvc可执行程序

前提是gcc环境和下载了jdk。先下载jsvc的c语言源码commons-daemon-1.2.3-native-src.tar.gz。解压,命令行进入到unix目录下,执行 ./configure --with-java=/usr/java path to java jsvc中引用了jni.h头文件,依赖jdk。 然后执行make编译,最后生成一个jsvc的二进制可执行程序。

java类实现org.apache.commons.daemon.Daemon接口

包括一下方法:
void init(String[] arguments): Here open configuration files, create a trace file, create ServerSockets, Threads
void start(): Start the Thread, accept incoming connections
void stop(): Inform the Thread to terminate the run(), close the ServerSockets
void destroy(): Destroy any object created in init()

调用jscv 执行命令./jsvc -cp commons-daemon.jar:my.jar MyClass

cp指class路径 MyClass是实现了Daemon接口的启动类
加上参数-stop则可以调用destroy()方法,停止java虚拟机进程

使用jsvc启动SpringBoot项目,启动配置类如下:

import org.apache.commons.daemon.Daemon;
import org.apache.commons.daemon.DaemonContext;
import org.apache.commons.daemon.support.DaemonLoader;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableAutoConfiguration
@ComponentScan
@EnableConfigurationProperties
public class Application implements Daemon {
    private ConfigurableApplicationContext ctx;
    private String[] args;

    @Override
    public void init(DaemonContext context) throws Exception {
        args = context.getArguments();
    }
    @Override
    public void start() throws Exception {
        ctx = SpringApplication.run(Application.class, args);
    }
    @Override
    public void stop() throws Exception {
        ctx.stop();
    }
    @Override
    public void destroy() {
        ctx.close();
    }

    // Main - mostly for development.
    public static void main(String[] args) throws Exception {
        System.err.println("WARNING - running as current user");
        DaemonLoader.Context ctx = new DaemonLoader.Context();
        Application app = new Application();
        ctx.setArguments(args);
        app.init(ctx);
        app.start();
    }
}

在执行命令./jsvc -cp springboot-jar-path : MyApplication-0.1.jar Application时候注意springboot依赖的jar包路径。可以将项目依赖的jar包拷贝出来,使用maven插件

   <plugin>
   <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-dependency-plugin</artifactId>
      <executions>
       <execution>
       <id>copy-dependencies</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>
                                ${directory}/lib/
                            </outputDirectory>
                        </configuration>
                    </execution>
                </executions>   
            </plugin>                         

jsvc原理

jsvc会启动3个进程:启动进程、控制进程、java服务进程。
启动进程,fork子进程:

main()
{
  fork()
  parent: wait_child(), wait until JAVA service started when the child says "I am ready".
  child: controller process.
}

控制进程:和java进程通信,控制java进程的启动停止等。

 while (fork()) {
    parent: wait_for_child.
      if exited and restart needed continue
      else exit.
    child: exit(child()). controlled process.
  }

java服务进程:

In child(): controlled process.
  init_JVM().
  load_service().
  start_service().
  say "I am ready"
  wait for signal or poll for stop
  stop_service().
  destroy_service().
  destroy_JVM().
  exit (with different codes so that parent knows if it has to restart us).

启动虚拟机,调用实现Daemon接口的启动类的int(),start()等方法。
在头文件java.h中定义了如下方法:

char *java_library(arg_data *args, home_data *data);
bool java_init(arg_data *args, home_data *data);
bool java_destroy(void);
bool java_load(arg_data *args);
bool java_signal(void);
bool java_start(void);
bool java_stop(void);
bool java_version(void);
bool java_check(arg_data *args);
bool JVM_destroy(int exit);

在java.c中引用头文件<jni.h>,通过虚拟暴露的jni接口与虚拟机通信,启动虚拟机,调用java方法。

为什么使用jsvc

  • 一个JAVA应用可能包括多个虚拟机进程,通过jsvc可以方便管理应用中的不同java进程。
  • java进程启动、停止或者退出时,执行init stop destroy等方法,java应用能通过这些方法。执行一下必要的操作。例如:Servlet容器在java进程退出时,需要将session序列化到硬盘。