【问题标题】:How to make RUST run gracefully in the background and daemonize?如何让 RUST 在后台优雅运行并进行守护?
【发布时间】:2020-08-23 12:42:54
【问题描述】:

这就是我想要实现的目标

root> ./webserver start   // Does not block the terminal after startup, runs in the background and the process is guarded
root> 

我目前的实现逻辑:

后台运行的逻辑
 use std::process::Command;
use std::thread;
use std::env;
use std::time::Duration;

fn main() {
    let args: Vec<String> = env::args().collect();
    if args.len() == 2 {
        if &args[1] == "start" {
            // Main process start child process
            let child = Command::new(&args[0])
                .spawn().expect("Child process failed to start.");
            println!("child pid: {}", child.id());
            // Main process exit
        }
    } else {Is there any more elegant approach? Looking forward to your reply
        // Main business logic
        run webserver
    }
}

这样,rust会在后台运行,不会阻塞终端,但rust在后台打印的信息仍然会显示在终端上,退出终端时会退出当前的rust程序

进程守护程序逻辑

我的想法是监控系统的退出信号,不处理退出请求

SIGHUP       1          /* Hangup (POSIX).  */                  
SIGINT       2          /* Interrupt (ANSI).  */                       
SIGQUIT      3          /* Quit (POSIX).  */                        
SIGTERM      15         /* Termination (ANSI).  */               

代码:

use signal_hook::{iterator::Signals, SIGHUP,SIGINT,SIGQUIT,SIGTERM};
use std::{thread, time::Duration};
pub fn process_daemon() {
    let signals = match Signals::new(&[SIGHUP,SIGINT,SIGQUIT,SIGTERM]) {
        Ok(t) => t,
        Err(e) => panic!(e),
    };Is there any more elegant approach? Looking forward to your reply

    thread::spawn(move || {
        for sig in signals.forever() {
            println!("Received signal {:?}", sig);
        }
    });

    thread::sleep(Duration::from_secs(2));
}

还有更优雅的方法吗?期待您的回复。

【问题讨论】:

  • 如果您可以使用像systemd 这样的流程管理器,而不是尝试在代码中解决它。这比尝试自己处理fork 或子进程要痛苦得多。
  • 一个本身会将其视为守护进程的程序将立即被我的系统禁止。没有任何程序能做到这一点。
  • 这似乎是你的 shell 的原生 job control 功能的任务。只需使用&amp; 后缀(在与sh 兼容的shell 中)开始您的工作,它将作为后台作业运行,或者正常启动,然后按Control-Z 暂停该进程,然后使用@ 在后台恢复它987654330@。查看您的 shell 文档以了解其他作业控制工具。正如@Matthias247 建议的那样,对于比临时运行更多的“生产”任务,请使用适当的服务管理器。
  • daemon(3) 呢?

标签: service rust daemon


【解决方案1】:

TLDR:如果您真的希望您的流程像服务一样运行(并且永不退出),可能需要设置一个服务管理器。否则,就让它成为一个正常的过程。

守护进程

马上要注意的一点是,大部分关于守护进程的考虑与作为一门语言的 Rust 无关,更多的是:

  1. 您的进程所针对的底层系统
  2. 您的守护进程在生成后的确切行为

通过查看您的问题,您似乎已经意识到其中的大部分内容。不幸的是,要正确回答您的问题,我们必须深入研究流程的复杂性以及它们的管理方式。应该注意的是,如果您对启动基础架构中的重要平台依赖性感到满意,那么现有的“服务”管理器是一个很好的解决方案。

如您所见,如果您想要一个简单的部署就可以工作(前提是它是为相关系统编译的),这绝非易事。这些只是裸机服务管理器。如果你想支持虚拟环境,你将不得不考虑 Kubernetes 服务、dockerizing 等。

之所以存在这些工具,是因为管理系统上长期运行的进程需要考虑很多因素:

  • 如果我的守护进程被杀死(或者系统重新启动),我的守护进程是否应该像服务一样运行并重新生成?上面的工具可以做到这一点。
  • 如果是一项服务,我的守护程序是否应该具有与之关联的状态状态以帮助维护?这有助于管理中断和构建工具以进行水平扩展。
  • 如果守护进程不应该是一个服务(在你的情况下不太可能给定二进制文件的名称),那么还有更多问题:它是否应该附加到父进程?是否应该附加到登录进程组?

鉴于这可能变得多么复杂,我对您的流程的猜测,只需直接运行该流程即可。根本不要守护进程。

对于测试,(如果您在类似 unix 的环境中)您可以在后台运行您的进程:

./webserver start &

这将在后台生成新进程,但会将其附加到 shell 的进程列表中。这对测试很有好处,因为如果那个 shell 消失了,系统会连同它一起清理这些附加的进程。

以上内容会将 stderr 和 stdout 文件描述符引导回您的终端并打印它们。如果您想避免这种情况,您可以随时redirect 在其他地方输出。

对这样的进程禁用信号对我来说似乎不是正确的方法。一旦您需要保存状态或向客户端发送终止消息,请保存这些信号以优雅地退出您的进程。如果您执行上述操作,您的守护程序将只能通过 kill -9 &lt;pid&gt; 或重新启动或找到一些您尚未覆盖的默认行为是终止的非标准信号来杀死。

【讨论】:

    猜你喜欢
    • 2011-01-04
    • 1970-01-01
    • 1970-01-01
    • 2016-11-12
    • 2017-03-26
    • 1970-01-01
    • 1970-01-01
    • 2021-02-23
    • 1970-01-01
    相关资源
    最近更新 更多