File size: 5,208 Bytes
f0574c7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57e423f
 
f0574c7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
#!/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()