启动后即在后台运行,并保存pid,可以查询状态

This commit is contained in:
zqc
2026-02-26 15:45:26 +08:00
parent 04bd6d3e15
commit 36cc5949d3

View File

@@ -1,11 +1,15 @@
# main_start.py
# 主启动脚本:读取配置并通过 subprocess 启动 rtsp_service_ws_kadian.py
# 支持 start 和 status 命令,默认执行 start
import json
import yaml
import base64
import subprocess
import sys
import os
import signal
import argparse
from typing import List
from common.camera_config import CameraConfig
@@ -13,6 +17,9 @@ from utils.logger import get_logger
logger = get_logger(__name__)
# PID 文件路径
PID_FILE = "rtsp_service.pid"
def load_cameras_from_yaml(config_path: str = "config.yaml") -> List[dict]:
"""从 YAML 文件加载摄像头配置"""
@@ -28,7 +35,7 @@ def cameras_to_base64_json(cameras: List[dict]) -> str:
def start_rtsp_service(cameras_base64: str, script_path: str = "rtsp_service_ws_kadian.py"):
"""启动 RTSP 服务子进程"""
"""启动 RTSP 服务子进程(后台运行)"""
cmd = [
sys.executable, # 当前 Python 解释器路径
script_path,
@@ -37,34 +44,117 @@ def start_rtsp_service(cameras_base64: str, script_path: str = "rtsp_service_ws_
logger.info(f"[INFO] Starting RTSP service with command: python {script_path} --cameras <base64_config>")
# 使用 Popen 启动子进程,保持运行
process = subprocess.Popen(cmd)
# 使用 start_new_session=True 创建新会话,类似 nohup 效果
process = subprocess.Popen(cmd, start_new_session=True)
return process
if __name__ == "__main__":
config_path = "config.yaml"
def save_pid(pid: int):
"""保存进程ID到文件"""
try:
with open(PID_FILE, "w") as f:
f.write(str(pid))
logger.info(f"[INFO] Saved PID {pid} to {PID_FILE}")
except Exception as e:
logger.error(f"[ERROR] Failed to save PID file: {e}")
def read_pid():
"""从PID文件读取进程ID"""
try:
with open(PID_FILE, "r") as f:
return int(f.read().strip())
except FileNotFoundError:
logger.warning(f"[WARN] PID file {PID_FILE} not found")
return None
except Exception as e:
logger.error(f"[ERROR] Failed to read PID file: {e}")
return None
def is_process_running(pid: int):
"""检查进程是否在运行"""
try:
# 发送信号0不实际发送信号仅检查进程是否存在
os.kill(pid, 0)
return True
except OSError:
return False
def start_service():
"""启动服务"""
# 检查是否已经在运行
pid = read_pid()
if pid and is_process_running(pid):
logger.warning(f"[WARN] Service is already running with PID {pid}")
return False
config_path = "config.yaml"
# 1. 读取配置
cameras_data = load_cameras_from_yaml(config_path)
logger.info(f"[INFO] Loaded {len(cameras_data)} cameras from {config_path}")
if not cameras_data:
logger.error("[ERROR] No cameras found in config, exiting...")
sys.exit(1)
return False
# 2. 转换为 base64 JSON
cameras_base64 = cameras_to_base64_json(cameras_data)
# 3. 启动子进程
process = start_rtsp_service(cameras_base64)
# 4. 等待子进程结束
try:
process.wait()
except KeyboardInterrupt:
logger.info("[INFO] Received interrupt, terminating subprocess...")
process.terminate()
process.wait()
logger.info("[INFO] Subprocess terminated")
process = start_rtsp_service(cameras_base64)
# 等待一下确保进程启动
import time
time.sleep(1)
# 4. 保存PID
save_pid(process.pid)
logger.info(f"[INFO] Service started successfully with PID {process.pid}")
logger.info("[INFO] Main process exiting, service will continue running in background")
return True
except Exception as e:
logger.error(f"[ERROR] Failed to start service: {e}")
return False
def status_service():
"""检查服务状态"""
pid = read_pid()
if not pid:
logger.info("[INFO] Service is not running (no PID file)")
return False
if is_process_running(pid):
logger.info(f"[INFO] Service is running with PID {pid}")
return True
else:
logger.info(f"[INFO] Service is not running (PID {pid} not found), cleaning up PID file")
try:
os.remove(PID_FILE)
except:
pass
return False
def main():
parser = argparse.ArgumentParser(description="RTSP Service Manager")
parser.add_argument("command", nargs="?", choices=["start", "status"], default="start",
help="Command to execute: start, status (default: start)")
args = parser.parse_args()
if args.command == "start":
success = start_service()
sys.exit(0 if success else 1)
elif args.command == "status":
success = status_service()
sys.exit(0 if success else 0) # 状态检查总是返回0退出码
if __name__ == "__main__":
main()