【Python量化系统工程化 #08】关了 SSH 就停?systemd 让脚本开机自启 + 异常自动拉起
系列定位:让"写一个能稳定跑半年的量化脚本"这件事,从散落的经验贴变成可复制的流水线。
痛点:脚本能跑了,但离开电脑就停
上一篇我们给脚本加上了定时调度。现在它能每天自动执行数据拉取和归档,看起来万事大吉。
但还有一个隐藏风险:运行环境不稳定。
- 笔记本合盖休眠 → Python 进程被系统挂起
- SSH 窗口关闭 → nohup 也保不住某些场景
- 云服务器重启 → 任务彻底消失,要手动登录重新启动
- 脚本偶发异常崩溃 → 没人知道它停了,直到发现数据断更
定时调度解决的是"什么时候跑",后台守护解决的是"跑了之后怎么一直活着"。
本文你将得到什么
- 为什么 nohup 和 screen 不是生产级方案
- 用 Python 模拟守护进程:主进程监控 + 子进程崩溃自动重启(跨平台演示)
- Linux 生产环境标准方案:systemd unit 配置详解
- 日志查看与排错:journalctl 常用命令
先做一个跨平台演示:Python 进程守护
在深入 systemd 之前,先用一段纯 Python 代码理解"守护"的本质:
一个进程(daemon)持续监控另一个进程(worker),发现 worker 挂了立刻重新启动。
!/usr/bin/env python3
-- coding: utf-8 --
"""
进程守护 demo:主进程监控 worker,异常退出时自动重启
"""
import os
import sys
import time
import subprocess
from datetime import datetime
def now():
return datetime.now().strftime("%H:%M:%S")
def worker():
"""模拟工作进程:执行量化任务,12 秒后模拟异常退出"""
pid = os.getpid()
print(f"[{now()}] [worker pid={pid}] 启动,开始执行任务...")
time.sleep(12)
print(f"[{now()}] [worker pid={pid}] 模拟异常崩溃!")
sys.exit(1)
def daemon(duration_sec=35):
"""守护进程:持续监控 worker,发现退出立即重启"""
print(f"[{now()}] [daemon] 启动,策略:worker 异常退出 -> 立即重启")
start = time.time()
worker_proc = None
restart_count = 0
while time.time() - start < duration_sec:
if worker_proc is None or worker_proc.poll() is not None:
if worker_proc is not None:
rc = worker_proc.returncode
print(f"[{now()}] [daemon] 检测到 worker 退出 (rc={rc}),执行第 {restart_count + 1} 次重启...")
worker_proc = subprocess.Popen([sys.executable, file, "--worker"])
restart_count += 1
print(f"[{now()}] [daemon] worker 已重启,新 pid={worker_proc.pid}")
time.sleep(1)
清理
if worker_proc and worker_proc.poll() is None:
worker_proc.terminate()
worker_proc.wait(timeout=3)
print(f"[{now()}] [daemon] 演示结束。期间 worker 共启动 {restart_count} 次")
if name == "main":
if "--worker" in sys.argv:
worker()
else:
daemon(duration_sec=35)
运行效果:
[10:27:07] [daemon] 启动,策略:worker 异常退出 -> 立即重启
[10:27:07] [daemon] worker 已重启,新 pid=26988
[10:27:19] [worker pid=26988] 模拟异常崩溃!
[10:27:20] [daemon] 检测到 worker 退出 (rc=1),执行第 2 次重启...
[10:27:20] [daemon] worker 已重启,新 pid=32628
核心逻辑只有三行:
- subprocess.Popen() 启动 worker
- worker_proc.poll() 检查是否还在运行
- 如果退出了,回到第 1 步重新启动
这就是所有进程守护的本质——包括 systemd。
生产环境方案:systemd(Linux)
上面的 Python 模拟只在"理解原理"时有用。生产环境不需要自己造轮子,Linux 的 systemd 已经提供了完整、健壮的进程管理能力。
- 创建 systemd unit 文件
/etc/systemd/system/quant-data.service:
[Unit]
Description=量化数据采集服务
After=network.target
[Service]
Type=simple
User=quant
WorkingDirectory=/home/quant/app
Environment=MAIRUI_LICENCE=LICENCE-66D8-9F96-0C7F0FBCD073
ExecStart=/home/quant/venv/bin/python /home/quant/app/run.py
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
- 关键配置项解释
配置项 作用
Type=simple 前台运行(Python 脚本无需 daemon 化)
Restart=always 无论退出码是什么,都自动重启
Restart=on-failure 仅异常退出(非零码)时重启
RestartSec=5 崩溃后等待 5 秒再重启,避免刷爆 CPU
Environment= 注入环境变量,证书不用写进代码
量化场景推荐 Restart=on-failure。如果脚本正常退出(比如跑完了当日全部任务),不需要强制重启;只有异常崩溃时才重启。
-
启动与查看状态
重载配置
sudo systemctl daemon-reload
开机自启 + 立即启动
sudo systemctl enable --now quant-data
查看运行状态
sudo systemctl status quant-data
实时查看日志
sudo journalctl -u quant-data -f
查看今日全部日志
sudo journalctl -u quant-data --since today
Windows 用户怎么办?
如果你的开发机是 Windows,有两条路径:
路径 A:继续开发调试
- 用上面的 Python daemon() 演示方案临时守护
- 或写一个简单的 .bat 脚本放在"启动文件夹"
路径 B:部署到 Linux 服务器
- 量化脚本最终还是要 7×24 运行,笔记本不是长久之计
- 一台最低配云服务器(2核2G)足够跑个人量化任务
- 部署后用 systemd 管理,配合 cron 做定时调度
推荐:开发在 Windows,部署用 Linux + systemd。
常见坑
坑 现象 解决
Restart=always 把正常退出也重启 脚本执行完当日任务后正常退出,systemd 又把它拉起来 改用 Restart=on-failure,或在脚本末尾循环等待而不是直接退出
环境变量没传进去 脚本读不到 MAIRUI_LICENCE 写在 [Service] 的 Environment= 里,不要用 ~/.bashrc
日志找不到 不知道脚本输出了什么 用 journalctl -u quant-data -f 实时查看
权限不足 systemctl start 失败 检查 User= 是否有权限访问工作目录和 Python 环境
虚拟环境路径不对 ImportError ExecStart 写虚拟环境的完整 python 路径,如 /home/quant/venv/bin/python
小结
- 守护的本质:一个进程监控另一个进程,死了就重启
- 开发调试:上面的 Python daemon() 代码足够理解原理
- Linux 生产环境:systemd 是标准答案,Restart=on-failure + journalctl 排错
- Windows:建议最终部署到 Linux 服务器,本地仅做开发
下篇预告:脚本稳定跑了,但日志文件越写越大、出错时大海捞针怎么办?#09 日志轮转与结构化日志,让排错不再头疼。
代码与文档
本文配套代码已开源在 GitHub:https://github.com/MaiRuiApi
安装 SDK:
pip install mairui
证书获取:访问麦蕊官网申请免费版或付费版 API 证书。
免责声明:本文仅供技术学习交流,不构成任何投资建议。股市有风险,入市需谨慎。

浙公网安备 33010602011771号