首页 > 系统相关 >文盘Rust -- 把程序作为守护进程启动

文盘Rust -- 把程序作为守护进程启动

时间:2022-11-02 10:44:37浏览次数:59  
标签:fork daemon -- server start daemonize let 文盘 Rust

当我们写完一个服务端程序,需要上线部署的时候,或多或少都会和操作系统的守护进程打交道,毕竟谁也不希望shell关闭既停服。今天我们就来聊聊这个事儿。

最早大家部署应用的通常操作是 “nohup xxxx &”,别说像weblogic 或者其他java 容器有启动脚本,里面其实也差不多;很喜欢 nginx的 -d 参数,或者像redis 配置文件里可以指定是否以守护进程启动。看起来很优雅。

那么,使用rust 写一个服务端程序能不能优雅的使用一个参数指定应用 daemon 模式启动,同时使用stop 方式优雅的停机呢?我们通过一个例子来说说基本的实现方式。

实例代码依然集成在[interactcli-rs](https://github.com/jiashiwen/interactcli-rs)工程中。

首先来模拟一个启动的服务进程 /src/server/server.rs

pub fn start(prefix: String) {
    for i in 0..1000 {
        println!("{}", prefix.clone() + &i.to_string());
        thread::sleep(Duration::from_secs(1));
    }
}

 

 程序每秒输出一个字符串,持续999秒,这个时间足够验证实验结果了。

后台启动有两个实现,分别是利用[fork](github.com/immortal/fork) 或 [daemonize](github.com/knsd/daemonize),这两个crate 实现原理类似,但在使用上稍有不同。

/src/cmd/cmdserver.rs,构建了两个启动子命令,分别来调用 fork 和 daemonize的守护进程启动实现.

 

pub fn new_server_cmd() -> Command {
    clap::Command::new("server")
        .about("server")
        .subcommand(server_start_byfork())
        .subcommand(server_start_bydaemonize())
}

pub fn server_start_byfork() -> Command {
    clap::Command::new("byfork")
        .about("start daemon by fork crate")
        .arg(
            Arg::new("daemon")
                .short('d')
                .long("daemon")
                .action(ArgAction::SetTrue)
                .help("start as daemon")
                .required(false),
        )
}
pub fn server_start_bydaemonize() -> Command {
    clap::Command::new("bydaemonize")
        .about("start daemon by daemonize crate")
        .arg(
            Arg::new("daemon")
                .short('d')
                .long("daemon")
                .action(ArgAction::SetTrue)
                .help("start as daemon")
                .required(false),
        )
}

 

 server 的子命令 byfork 启动 通过 fork 实现的功能,bydaemonize 则调用通过 daemonize 的功能实现。

命令解析的代码在 /src/cmd/rootcmd.rs 文件中。

先来看看基于 fork 的实现: 

if let Some(startbyfork) = server.subcommand_matches("byfork") {
    println!("start by fork");
    if startbyfork.get_flag("daemon") {
        let args: Vec<String> = env::args().collect();
        if let Ok(Fork::Child) = daemon(true, false) {
            // 启动子进程
            let mut cmd = Command::new(&args[0])
            for idx in 1..args.len() {
                let arg = args.get(idx).expect("get cmd arg error!");
                // 去除后台启动参数,避免重复启动
                if arg.eq("-d") || arg.eq("-daemon") {
                    continue;
                }
                cmd.arg(arg);
            
            let child = cmd.spawn().expect("Child process failed to start.");
            fs::write("pid", child.id().to_string()).unwrap();
            println!("process id is:{}", std::process::id());
            println!("child id is:{}", child.id());
        }
        println!("{}", "daemon mod");
        process::exit(0);
    }
    start("by_fork:".to_string());
}

 

首先,通过 Fork::daemon 函数派生出一个子进程;然后解析一下当前命令,去掉 -d 参数,构建一个启动命令,子命令启动,退出父进程。这基本符合操作系统创建守护进程的过程 -- 两次 fork。

再来看看基于 daemonize 的实现:

 

if let Some(startbydaemonize) = server.subcommand_matches("bydaemonize") {
            println!("start by daemonize");
            let base_dir = env::current_dir().unwrap();
            if startbydaemonize.get_flag("daemon") {
                let stdout = File::create("/tmp/daemon.out").unwrap();
                let stderr = File::create("/tmp/daemon.err").unwrap();

                println!("{:?}", base_dir);

                let daemonize = Daemonize::new()
                    .pid_file("/tmp/test.pid") // Every method except `new` and `start`
                    .chown_pid_file(true) // is optional, see `Daemonize` documentation
                    .working_directory(base_dir.as_path()) // for default behaviour.          
                    .umask(0o777) // Set umask, `0o027` by default.
                    .stdout(stdout) // Redirect stdout to `/tmp/daemon.out`.
                    .stderr(stderr) // Redirect stderr to `/tmp/daemon.err`.
                    .privileged_action(|| "Executed before drop privileges");

                match daemonize.start() {
                    Ok(_) => {
                        println!("Success, daemonized");
                    }
                    Err(e) => eprintln!("Error, {}", e),
                }
            }
            println!("pid is:{}", std::process::id());
            fs::write("pid", process::id().to_string()).unwrap();
            start("by_daemonize:".to_string());
        }

 

 首先获取当前的工作目录,默认情况下 daemonize 会将工作目录设置为 "/",为了避免权限问题,我们获取当前目录作为守护进程的工作目录。不知道是什么原因,在配置了pid_file 后,启动守护进程时并没在文件中有记录 pid。不过也没关系,我们可以在外部获取并记录守护进程的pid。

两种方式启动的守护进程均可在关闭shell的情况下维持进程运行。

从实现上来讲,不论是 fork 还是 daemonize 都是 通过unsafe 方式调用了 libc api,类 unix 系统大多跑起来没问题,windows 系统作者没有验证。

本期关于守护进程的话题就聊到这儿。

咱们下期见。

作者:贾世闻

 

标签:fork,daemon,--,server,start,daemonize,let,文盘,Rust
From: https://www.cnblogs.com/Jcloud/p/16850261.html

相关文章

  • 5.流程控制
    在编程语言中,程序总是自上而下的执行,即按照顺序的方式执行,但是我们可以通过一些语句控制程序的执行流程,即条件控制和循环控制1.条件控制在python中条件控制的关键字有 i......
  • MySQL_语法规范
    1不区分大小写,但建议关键字大写,表名、列名小写2每条命令最好用分号结尾3每条命令根据需要,可以进行缩进或换行4注释      单行注释:#      单行注释:......
  • C# 语法分析器(五)错误恢复
    系列导航(一)语法分析介绍(二)LR(0)语法分析(三)LALR语法分析(四)二义性文法(五)错误恢复(六)构造语法分析器语法分析中的错误恢复是一个很复杂的问题,有多种可能的错误恢复......
  • C# 语法分析器(四)二义性文法
    系列导航(一)语法分析介绍(二)LR(0)语法分析(三)LALR语法分析(四)二义性文法(五)错误恢复(六)构造语法分析器二义性文法,指的是一个可以为某个句子生成多颗语法分析树。最常......
  • git远端分支合并错误,需要回退到之前的分支
    工作中经常遇到gitcommit后(尚未gitpush操作),需要回退的情况具体方法如下:1.执行gitlog找到本次commi的ID信息比如commitid信息为:90f1ce4d73c5dc63f46fa61984a6bb878f47......
  • Plan
    1.数据结构自学:莫队复习:线段树,树状数组,分块,ST表(?2.dp自学:数位dp,换根dp复习:区间dp,树形dp,状压dp3.图论自学:二分图,网络流4.数学自学:全套5.字符串自学:KMP复习:trie树......
  • C# 语法分析器(三)LALR 语法分析
    系列导航(一)语法分析介绍(二)LR(0)语法分析(三)LALR语法分析(四)二义性文法(五)错误恢复(六)构造语法分析器上一章构造了LR(0)自动机,现在就可以来构造LALR语法分析表了......
  • 开发用户K8S授权
    #开发用户没有K8S权限[ans@master~]$kubectlgetpoUnabletoconnecttotheserver:x509:certificatesignedbyunknownauthority(possiblybecauseof"crypt......
  • C# 语法分析器(二)LR(0) 语法分析
    系列导航(一)语法分析介绍(二)LR(0)语法分析(三)LALR语法分析(四)二义性文法(五)错误恢复(六)构造语法分析器首先,需要介绍下LALR语法分析的基础:LR(0)语法分析。还是以之......
  • C# 语法分析器(一)语法分析介绍
    系列导航(一)语法分析介绍(二)LR(0)语法分析(三)LALR语法分析(四)二义性文法(五)错误恢复(六)构造语法分析器之前的《C#词法分析器》系列,已经可以构造出一个词法分析器,可以......