当我们写完一个服务端程序,需要上线部署的时候,或多或少都会和操作系统的守护进程打交道,毕竟谁也不希望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 系统作者没有验证。

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

咱们下期见。

作者:贾世闻

文盘Rust -- 把程序作为守护进程启动的更多相关文章

  1. Redis使用守护进程启动sentinel并指定其日志目录

    正常redis-server可以通过配置文件来指定守护进程启动以及指定日志路径,但sentinel就不一样了.正常启动redis的sentinel时,进程会直接在前台跑,一退出sentinel进程就关 ...

  2. python程序配置守护进程

    参考博客 python Supervisor 使用与配置_a35155的博客-CSDN博客 Ubuntu系统下:apt-get install supervisor,通过这种方式安装后,自动设置为开机 ...

  3. 为程序启用 守护进程-- supervisior

    待补充... Add this to your /etc/supervisord.conf: [rpcinterface:supervisor] supervisor.rpcinterface_fac ...

  4. Ubuntu上使用systemd创建服务文件来启动和监视底层网络应用程序实现守护进程

    在Linux上使用Nginx设置ASP.NET Core的托管环境,并部署到它 创建服务文件 创建服务定义文件: sudo vim /etc/systemd/system/kestrel-basic. ...

  5. 文盘Rust -- struct 中的生命周期

    最近在用rust 写一个redis的数据校验工具.redis-rs中具备 redis::ConnectionLike trait,借助它可以较好的来抽象校验过程.在开发中,不免要定义struct 中的 ...

  6. Kafka集群部署 (守护进程启动)

    1.Kafka集群部署 1.1集群部署的基本流程 下载安装包.解压安装包.修改配置文件.分发安装包.启动集群 1.2集群部署的基础环境准备 安装前的准备工作(zk集群已经部署完毕)  关闭防火墙 c ...

  7. linus 下redis守护进程启动

    修改配置文件 sudo vim /usr/src/redis/redis.conf // 具体的安装目录不一样,以安装的时候为准 # 将daemonize 改为yes daemonize yes 重新 ...

  8. Python编写守护进程程序

    Python编写守护进程程序思路 1. fork子进程,父进程退出通常,我们执行服务端程序的时候都会通过终端连接到服务器,成功连接后会加载shell环境,终端和shell都是进程,shell进程是终端 ...

  9. PHP程序守护进程化

    一般Server程序都是运行在系统后台,这与普通的交互式命令行程序有很大的区别.glibc里有一个函数daemon.调用此函数,就可使当前进程脱离终端变成一个守护进程,具体内容参见man daemon ...

随机推荐

  1. Java精进-20分钟学会mybatis使用

    文字分享 希望现在的你无论有明确具体的目标还是没有,都能重视自己的需求和目标,并且常常回顾,或许可以找一个你习惯的方式写出来,挂在哪里,电脑或日记本都好.当你疲惫或迷茫的时候拿出来看一下,这在情怀领域 ...

  2. RabbitMQ实现订单超时案例

    前言 人间清醒 目录 前言 业务场景 JUC(DelayQueue)方案 DelayQueue简介 JUC DelayQueue实现订单超时案例代码 案例代码 Redis Key过期事件方案 简介 R ...

  3. WebGPU实现Ray Packet

    大家好~本文在如何用WebGPU流畅渲染百万级2D物体?基础上进行优化,使用WebGPU实现了Ray Packet,也就是将8*8=64条射线作为一个Packet一起去访问BVH的节点.这样做的好处是 ...

  4. nginx的安装和配置

    目录 目录 一.购买下载SSL证书 二.修改Nginx配置信息 三.重启Nginx 一.购买下载SSL证书 SSL证书阿里云做活动期间可以免费申请,购买SSL证书时选择单域名-DV SSL-免费版即可 ...

  5. Vben Admin 源码学习:状态管理-角色权限

    前言 本文将对 Vue-Vben-Admin 角色权限的状态管理进行源码解读,耐心读完,相信您一定会有所收获! 更多系列文章详见专栏   Vben Admin 项目分析&实践 . 本文涉及到角 ...

  6. Java的lamda表达式/函数式接口/流式计算

    在我们看他人code的时候经常会看到,可能会经常看到lambda表达式,函数式接口,以及流式计算.在刚接触这些新功能时,也觉得真的有必要吗?但是现在写多了,发现这个功能确实能简化代码结构,提升编码效率 ...

  7. Odoo自建应用初步总结(一)

    学习了<Odoo快速入门与实践 Python开发ERP指南>(刘金亮 2019年5月第1版 机械工业出版社)第6章自建应用入门后进行一下总结. 因为本书作者使用Odoo11,而目前最新版本 ...

  8. 更改elasticsearch中索引的mapping

    文章转载自:https://www.cnblogs.com/uglyliu/p/12331964.html 昨天研发说在kibana中统计userid字段不出图,后来查到该字段显示冲突了,然后再查看了 ...

  9. es,logstash各版本对应要求的JDK版本,操作系统对应示意图

    官网地址:https://www.elastic.co/cn/support/matrix

  10. Beats:使用Elastic Stack对Nginx Web服务器监控