diff --git a/app.py b/app.py index d6e415d..42c4308 100755 --- a/app.py +++ b/app.py @@ -3444,15 +3444,15 @@ if __name__ == '__main__': print("默认管理员: admin/admin") print("=" * 60 + "\n") - # 同步初始化浏览器池(必须在socketio.run之前,否则eventlet会导致asyncio冲突) + # 初始化浏览器工作线程池(按需模式,启动时不创建浏览器) try: system_cfg = database.get_system_config() pool_size = system_cfg.get('max_screenshot_concurrent', 3) if system_cfg else 3 - print(f"正在预热 {pool_size} 个浏览器实例(截图加速)...") + print(f"初始化截图线程池({pool_size}个worker,按需启动浏览器,空闲5分钟后自动关闭)...") init_browser_worker_pool(pool_size=pool_size) - print("✓ 浏览器池初始化完成") + print("✓ 截图线程池初始化完成") except Exception as e: - print(f"警告: 浏览器池初始化失败: {e}") + print(f"警告: 截图线程池初始化失败: {e}") socketio.run(app, host=config.SERVER_HOST, port=config.SERVER_PORT, debug=config.DEBUG, allow_unsafe_werkzeug=True) diff --git a/browser_pool_worker.py b/browser_pool_worker.py index 18dfcc6..88cc95b 100755 --- a/browser_pool_worker.py +++ b/browser_pool_worker.py @@ -98,26 +98,33 @@ class BrowserWorker(threading.Thread): return self._create_browser() def run(self): - """工作线程主循环""" - self.log("Worker启动") - - # 初始创建浏览器 - if not self._create_browser(): - self.log("初始浏览器创建失败,Worker退出") - return + """工作线程主循环 - 按需启动浏览器模式""" + self.log("Worker启动(按需模式,等待任务时不占用浏览器资源)") + last_task_time = 0 + IDLE_TIMEOUT = 300 # 空闲5分钟后关闭浏览器 while self.running: try: - # 从队列获取任务(带超时,以便能响应停止信号) + # 从队列获取任务(带超时,以便能响应停止信号和空闲检查) self.idle = True - task = self.task_queue.get(timeout=1) + try: + task = self.task_queue.get(timeout=10) + except queue.Empty: + # 检查是否需要关闭空闲的浏览器 + if self.browser_instance and last_task_time > 0: + idle_time = time.time() - last_task_time + if idle_time > IDLE_TIMEOUT: + self.log(f"空闲{int(idle_time)}秒,关闭浏览器释放资源") + self._close_browser() + continue + self.idle = False if task is None: # None作为停止信号 self.log("收到停止信号") break - # 确保浏览器可用 + # 按需创建或确保浏览器可用 if not self._ensure_browser(): self.log("浏览器不可用,任务失败") task['callback'](None, "浏览器不可用") @@ -140,20 +147,19 @@ class BrowserWorker(threading.Thread): result = task_func(self.browser_instance, *task_args, **task_kwargs) callback(result, None) self.log(f"任务执行成功") + last_task_time = time.time() except Exception as e: self.log(f"任务执行失败: {e}") callback(None, str(e)) self.failed_tasks += 1 + last_task_time = time.time() # 任务失败后,检查浏览器健康 if not self._check_browser_health(): self.log("任务失败导致浏览器异常,将在下次任务前重建") self._close_browser() - except queue.Empty: - # 队列为空,继续等待 - continue except Exception as e: self.log(f"Worker出错: {e}") time.sleep(1) @@ -186,12 +192,12 @@ class BrowserWorkerPool: print(f"[浏览器池] {message}") def initialize(self): - """初始化工作线程池""" + """初始化工作线程池(按需模式,启动时不创建浏览器)""" with self.lock: if self.initialized: return - self.log(f"正在初始化工作线程池({self.pool_size}个worker)...") + self.log(f"正在初始化工作线程池({self.pool_size}个worker,按需启动浏览器)...") for i in range(self.pool_size): worker = BrowserWorker( @@ -202,11 +208,8 @@ class BrowserWorkerPool: worker.start() self.workers.append(worker) - # 等待所有worker准备就绪 - time.sleep(2) - self.initialized = True - self.log(f"✓ 工作线程池初始化完成({self.pool_size}个worker已就绪)") + self.log(f"✓ 工作线程池初始化完成({self.pool_size}个worker就绪,浏览器将在有任务时按需启动)") def submit_task(self, task_func: Callable, callback: Callable, *args, **kwargs) -> bool: """ diff --git a/database.py b/database.py index 42063b8..4ec9e34 100755 --- a/database.py +++ b/database.py @@ -1013,6 +1013,10 @@ def update_system_config(max_concurrent=None, schedule_enabled=None, schedule_ti updates.append('max_concurrent_per_account = ?') params.append(max_concurrent_per_account) + if max_screenshot_concurrent is not None: + updates.append('max_screenshot_concurrent = ?') + params.append(max_screenshot_concurrent) + if schedule_weekdays is not None: updates.append('schedule_weekdays = ?') params.append(schedule_weekdays) diff --git a/templates/admin.html b/templates/admin.html index 123a4e6..c895f2e 100644 --- a/templates/admin.html +++ b/templates/admin.html @@ -579,26 +579,26 @@