YifangTrans / launch.py
FangTan's picture
Update launch.py
57e423f verified
Raw
History Blame Contribute Delete
5.21 kB
#!/usr/bin/env python3
"""
启动脚本:同时运行 vLLM 服务器和 Streamlit 应用
保留原有的 Streamlit 入口,添加 vLLM 后台服务
"""
import subprocess
import time
import os
import signal
import sys
import threading
import logging
from pathlib import Path
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
# 添加项目根目录到路径
sys.path.append(str(Path(__file__).parent.parent))
class ServiceManager:
def __init__(self):
self.processes = []
self.running = True
def run_vllm(self):
"""启动 vLLM 服务器(后台服务)"""
logger.info("🚀 正在启动 vLLM 推理服务器...")
# vLLM 启动命令(优化显存使用)
cmd = [
"python", "-m", "vllm.entrypoints.openai.api_server",
"--model", "Qwen/Qwen3.5-27B-GPTQ-Int4",
"--port", "8000",
"--host", "0.0.0.0",
"--tensor-parallel-size", "1",
"--quantization", "gptq",
"--max-model-len", "32768",
"--gpu-memory-utilization", "0.85", # 保留 15% 显存给 Streamlit 等
"--enforce-eager",
"--max-num-batched-tokens", "8192",
"--max-num-seqs", "4",
"--disable-log-stats", # 减少日志输出
]
# 设置 CUDA 环境
env = os.environ.copy()
#env["CUDA_VISIBLE_DEVICES"] = "0"
#env["VLLM_USE_TRITON"] = "1" # 使用 Triton 加速
# 启动进程
process = subprocess.Popen(
cmd,
env=env,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
universal_newlines=True,
bufsize=1
)
self.processes.append(("vllm", process))
# 在单独的线程中读取输出
def log_output():
for line in process.stdout:
if line.strip():
logger.info(f"[vLLM] {line.strip()}")
threading.Thread(target=log_output, daemon=True).start()
# 等待 vLLM 就绪
self.wait_for_vllm()
def wait_for_vllm(self, max_retries=60):
"""等待 vLLM 服务器就绪"""
import requests
logger.info("⏳ 等待 vLLM 服务器初始化...")
for i in range(max_retries):
try:
response = requests.get("http://localhost:8000/health")
if response.status_code == 200:
logger.info("✅ vLLM 服务器已就绪!")
return True
except:
pass
# 显示进度
if i % 10 == 0:
logger.info(f"⏳ 仍在等待 vLLM... ({i+1}/{max_retries})")
time.sleep(2)
logger.error("❌ vLLM 启动超时")
return False
def run_streamlit(self):
"""启动 Streamlit 应用(主服务)"""
logger.info("🖥️ 正在启动 Streamlit 应用...")
# 使用原有的入口点
cmd = [
"streamlit", "run",
"streamlit_app.py",
"--server.port=8501",
"--server.address=0.0.0.0",
"--server.enableCORS=false",
"--server.enableXsrfProtection=false",
"--server.maxUploadSize=10", # 限制上传大小
]
process = subprocess.Popen(cmd)
self.processes.append(("streamlit", process))
logger.info("✅ Streamlit 应用已启动")
def monitor_services(self):
"""监控服务状态"""
while self.running:
time.sleep(10)
for name, process in self.processes:
if process.poll() is not None:
logger.error(f"❌ {name} 服务意外停止,退出码: {process.returncode}")
self.stop_all()
sys.exit(1)
def stop_all(self, signum=None, frame=None):
"""停止所有服务"""
logger.info("🛑 正在停止所有服务...")
self.running = False
for name, process in self.processes:
logger.info(f"正在停止 {name}...")
process.terminate()
# 等待进程结束
for name, process in self.processes:
try:
process.wait(timeout=5)
except subprocess.TimeoutExpired:
logger.warning(f"强制终止 {name}")
process.kill()
logger.info("✅ 所有服务已停止")
sys.exit(0)
def run(self):
"""运行所有服务"""
# 注册信号处理
signal.signal(signal.SIGINT, self.stop_all)
signal.signal(signal.SIGTERM, self.stop_all)
# 启动 vLLM(后台)
self.run_vllm()
# 启动 Streamlit(前台)
self.run_streamlit()
# 开始监控
self.monitor_services()
if __name__ == "__main__":
manager = ServiceManager()
manager.run()