文盘Rust -- 把程序作为守护进程启动
当我们写完一个服务端程序,需要上线部署的时候,或多或少都会和操作系统的守护进程打交道,毕竟谁也不希望shell关闭既停服。今天我们就来聊聊这个事儿。
最早大家部署应用的通常操作是 “nohup xxxx &”,别说像weblogic 或者其他java 容器有启动脚本,里面其实也差不多;很喜欢 nginx的 -d 参数,或者像redis 配置文件里可以指定是否以守护进程启动。看起来很优雅。
那么,使用rust 写一个服务端程序能不能优雅的使用一个参数指定应用 daemon 模式启动,同时使用stop 方式优雅的停机呢?我们通过一个例子来说说基本的实现方式。
实例代码依然集成在[interactcli-rs](https://github.com/jiashiwen/interactcli-rs)工程中。
首先来模拟一个启动的服务进程 /src/server/server.rs
```rust 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的守护进程启动实现。
```rust 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 的实现
```rust 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 的实现。
```rust 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 系统作者没有验证。
本期关于守护进程的话题就聊到这儿。
咱们下期见。
作者:贾世闻

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Sentry 向 190 多名开源维护者捐赠 26 万美元
开源 Sentry 商业公司计划在 2022 年向 193 +开源维护者(包含开源基金会、项目和个人)捐赠260028 美元,作为其成功商业化后对开源社区的回馈。 Sentry 是一个开源的跨平台实时应用监控系统,支持服务端与客户端的监控,还有个强大的后台错误分析、报警平台。其商业公司致力于回馈开源社区,此前曾在博客中宣称每位开源贡献者都值得 2000 美金的捐助。去年,Sentry向 108 名受赠者捐款了 15.5 万美元,今年捐赠额度则提高到260028 美元 。 据其博客介绍,目前 Sentry已通过传统捐赠渠道( GitHub Sponsors、Open Collective和直接付款)向 191 个基金会、项目和个人开源维护者提供了 241,380 美元赠款,此外还通过两个新的募捐平台Thanks.dev和StackAid认捐了 18,648 美元,所有的捐款将在接下来的三个月内支付,让维护者有充足的时间注册。 至于金额的分配,Sentry 对五个基金会捐赠了 67,500 美元,涵盖了用于交付 Sentry 的核心语言(Python、JavaScript 和 Rust)和...
- 下一篇
BizWorks 应用平台基于 KubeVela 的实践
作者: 靖立明 前言 BizWorks 与 KubeVela 的合作始于 1.0.5 版本,BizWorks 在 1.0.5 版本上完成了关键技术验证并且在 1.2.5 版本上基础上扩展了 BizWorks 的应用部署和运维能力。通过近一年多的深度合作,BizWorks 通过 KubeVela 解决了一些痛点和诉求,同时基于 KubeVela 功能和特性也沉淀了一些实践,本文将分别通过介绍 BizWorks 在 KubeVela 使用场景来讲述如何探索和实践云原生时代新一代 PaaS 平台持续交付能力的落地。 BizWorks 介绍 BizWorks(https://bizworks.aliyun.com/)是一体化的阿里云原生应用的开发和运营平台,内置阿里巴巴业务中台构建的最佳技术实践。产品主要包括:业务建模平台、业务应用平台、演练压测平台、能力运营平台、一体化运行和运维平台。BizWorks 提供的产品能力(图1-1),普遍适用于企业云原生应用高效开发以及企业业务能力沉淀和复用的场景。 图 1-1 BizWorks业务架构 BizWorks 一体化运行和运维平台提供一站式的应用生命...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- CentOS8安装Docker,最新的服务器搭配容器使用
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- Windows10,CentOS7,CentOS8安装Nodejs环境
- SpringBoot2全家桶,快速入门学习开发网站教程
- CentOS8编译安装MySQL8.0.19