新增run.py统一管理项目启动
This commit is contained in:
parent
fdb14dfa54
commit
191c25696b
@ -34,8 +34,17 @@ class Settings(BaseSettings):
|
||||
# 运行模式
|
||||
run_mode: str = 'api'
|
||||
|
||||
# 本地后端主机配置
|
||||
backend_host: str = '0.0.0.0'
|
||||
backend_port: int = 9001
|
||||
backend_reload: bool = True
|
||||
|
||||
# 异步任务队列(Dramatiq)配置
|
||||
worker_processes: int = 1
|
||||
worker_threads: int = 1
|
||||
|
||||
class Config:
|
||||
env_file = str(Path(__file__).parent.parent / '.env')
|
||||
env_file = str(Path(__file__).resolve().parents[3] / '.env')
|
||||
env_file_encoding = 'utf-8'
|
||||
case_sensitive = False
|
||||
|
||||
|
||||
121
apps/jingrow/jingrow/run.py
Normal file
121
apps/jingrow/jingrow/run.py
Normal file
@ -0,0 +1,121 @@
|
||||
"""
|
||||
统一启动入口:`python -m jingrow.run [app|dramatiq|all]`
|
||||
app - 读取 Config 启动 Uvicorn
|
||||
dramatiq - 读取 Config 以 exec 方式启动 Dramatiq CLI
|
||||
all - 默认;同时以子进程启动 app 与 dramatiq,并转发信号
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import signal
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
import uvicorn
|
||||
|
||||
from jingrow.config import Config
|
||||
|
||||
|
||||
def run_app() -> None:
|
||||
host = getattr(Config, "backend_host", "0.0.0.0")
|
||||
port = int(getattr(Config, "backend_port", 9001))
|
||||
reload_enabled = bool(getattr(Config, "backend_reload", True))
|
||||
|
||||
apps_dir = Path(__file__).resolve().parents[2]
|
||||
reload_dirs = [str(apps_dir)] if reload_enabled else None
|
||||
|
||||
# 使用导入字符串以启用 uvicorn 的 reload 能力并标准化启动行为
|
||||
uvicorn.run(
|
||||
"jingrow.main:app",
|
||||
host=host,
|
||||
port=port,
|
||||
reload=reload_enabled,
|
||||
reload_dirs=reload_dirs,
|
||||
)
|
||||
|
||||
|
||||
def run_dramatiq() -> None:
|
||||
processes = int(getattr(Config, "worker_processes", 1))
|
||||
threads = int(getattr(Config, "worker_threads", 1))
|
||||
|
||||
args = [
|
||||
"dramatiq",
|
||||
"jingrow.services.queue",
|
||||
"--processes",
|
||||
str(processes),
|
||||
"--threads",
|
||||
str(threads),
|
||||
]
|
||||
os.execvp("dramatiq", args)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(prog="jingrow.run", description="Jingrow 统一运行入口")
|
||||
parser.add_argument("role", nargs="?", choices=["app", "dramatiq", "all"], default="all", help="运行角色:app/dramatiq/all,默认 all")
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.role == "app":
|
||||
run_app()
|
||||
return
|
||||
if args.role == "dramatiq":
|
||||
run_dramatiq()
|
||||
return
|
||||
|
||||
# all:并行启动 app 与 dramatiq(作为子进程,便于优雅退出)
|
||||
children: list[subprocess.Popen] = []
|
||||
|
||||
def spawn(cmd: list[str]) -> subprocess.Popen:
|
||||
return subprocess.Popen(cmd)
|
||||
|
||||
api_cmd = [sys.executable, "-m", "jingrow.run", "app"]
|
||||
dramatiq_cmd = [sys.executable, "-m", "jingrow.run", "dramatiq"]
|
||||
|
||||
children.append(spawn(api_cmd))
|
||||
children.append(spawn(dramatiq_cmd))
|
||||
|
||||
def handle_signal(signum, frame):
|
||||
for p in children:
|
||||
if p.poll() is None:
|
||||
try:
|
||||
p.terminate()
|
||||
except Exception:
|
||||
pass
|
||||
# 二次强杀
|
||||
try:
|
||||
for p in children:
|
||||
try:
|
||||
p.wait(timeout=5)
|
||||
except Exception:
|
||||
if p.poll() is None:
|
||||
p.kill()
|
||||
finally:
|
||||
# 以第一个子进程退出码作为整体退出码
|
||||
code = next((p.returncode for p in children if p.returncode is not None), 0)
|
||||
os._exit(code)
|
||||
|
||||
signal.signal(signal.SIGINT, handle_signal)
|
||||
signal.signal(signal.SIGTERM, handle_signal)
|
||||
|
||||
# 等待任一子进程退出,随后清理另一个
|
||||
exit_code = 0
|
||||
try:
|
||||
while True:
|
||||
any_running = False
|
||||
for p in children:
|
||||
rc = p.poll()
|
||||
if rc is None:
|
||||
any_running = True
|
||||
else:
|
||||
exit_code = rc
|
||||
raise SystemExit
|
||||
if not any_running:
|
||||
break
|
||||
signal.pause()
|
||||
except SystemExit:
|
||||
handle_signal(signal.SIGTERM, None)
|
||||
finally:
|
||||
sys.exit(exit_code)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
14
dev.sh
14
dev.sh
@ -125,10 +125,9 @@ start_all() {
|
||||
info "启动所有服务..."
|
||||
# 使用延迟启动,让后端先启动3秒
|
||||
npx concurrently \
|
||||
--names "BACKEND,WORKER,FRONTEND" \
|
||||
--prefix-colors "blue,yellow,green" \
|
||||
"cd apps/jingrow && uvicorn jingrow.main:app --host 0.0.0.0 --port 9001 --reload $RELOAD_DIRS" \
|
||||
"cd apps/jingrow && dramatiq jingrow.services.queue --processes 1 --threads 1" \
|
||||
--names "BACKEND,FRONTEND" \
|
||||
--prefix-colors "blue,green" \
|
||||
"cd apps/jingrow && python3 -m jingrow.run" \
|
||||
"sleep 3 && cd apps/jingrow/frontend && npm run dev"
|
||||
}
|
||||
|
||||
@ -155,10 +154,9 @@ start_backend() {
|
||||
RELOAD_DIRS=$(build_reload_dirs)
|
||||
|
||||
npx concurrently \
|
||||
--names "BACKEND,WORKER" \
|
||||
--prefix-colors "blue,yellow" \
|
||||
"cd apps/jingrow && uvicorn jingrow.main:app --host 0.0.0.0 --port 9001 --reload $RELOAD_DIRS" \
|
||||
"cd apps/jingrow && dramatiq jingrow.services.queue --processes 1 --threads 1"
|
||||
--names "BACKEND" \
|
||||
--prefix-colors "blue" \
|
||||
"cd apps/jingrow && python3 -m jingrow.run"
|
||||
}
|
||||
|
||||
# 停止服务
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user