启动后即在后台运行,并保存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 # main_start.py
# 主启动脚本:读取配置并通过 subprocess 启动 rtsp_service_ws_kadian.py # 主启动脚本:读取配置并通过 subprocess 启动 rtsp_service_ws_kadian.py
# 支持 start 和 status 命令,默认执行 start
import json import json
import yaml import yaml
import base64 import base64
import subprocess import subprocess
import sys import sys
import os
import signal
import argparse
from typing import List from typing import List
from common.camera_config import CameraConfig from common.camera_config import CameraConfig
@@ -13,6 +17,9 @@ from utils.logger import get_logger
logger = get_logger(__name__) logger = get_logger(__name__)
# PID 文件路径
PID_FILE = "rtsp_service.pid"
def load_cameras_from_yaml(config_path: str = "config.yaml") -> List[dict]: def load_cameras_from_yaml(config_path: str = "config.yaml") -> List[dict]:
"""从 YAML 文件加载摄像头配置""" """从 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"): def start_rtsp_service(cameras_base64: str, script_path: str = "rtsp_service_ws_kadian.py"):
"""启动 RTSP 服务子进程""" """启动 RTSP 服务子进程(后台运行)"""
cmd = [ cmd = [
sys.executable, # 当前 Python 解释器路径 sys.executable, # 当前 Python 解释器路径
script_path, 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>") logger.info(f"[INFO] Starting RTSP service with command: python {script_path} --cameras <base64_config>")
# 使用 Popen 启动子进程,保持运行 # 使用 start_new_session=True 创建新会话,类似 nohup 效果
process = subprocess.Popen(cmd) process = subprocess.Popen(cmd, start_new_session=True)
return process return process
if __name__ == "__main__": def save_pid(pid: int):
config_path = "config.yaml" """保存进程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. 读取配置 # 1. 读取配置
cameras_data = load_cameras_from_yaml(config_path) cameras_data = load_cameras_from_yaml(config_path)
logger.info(f"[INFO] Loaded {len(cameras_data)} cameras from {config_path}") logger.info(f"[INFO] Loaded {len(cameras_data)} cameras from {config_path}")
if not cameras_data: if not cameras_data:
logger.error("[ERROR] No cameras found in config, exiting...") logger.error("[ERROR] No cameras found in config, exiting...")
sys.exit(1) return False
# 2. 转换为 base64 JSON # 2. 转换为 base64 JSON
cameras_base64 = cameras_to_base64_json(cameras_data) cameras_base64 = cameras_to_base64_json(cameras_data)
# 3. 启动子进程 # 3. 启动子进程
process = start_rtsp_service(cameras_base64)
# 4. 等待子进程结束
try: try:
process.wait() process = start_rtsp_service(cameras_base64)
except KeyboardInterrupt: # 等待一下确保进程启动
logger.info("[INFO] Received interrupt, terminating subprocess...") import time
process.terminate() time.sleep(1)
process.wait()
logger.info("[INFO] Subprocess terminated") # 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()