From 02e34960d3b611f46d40ce8890e5992cce016009 Mon Sep 17 00:00:00 2001 From: liuwei Date: Fri, 17 Apr 2026 11:27:27 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96md2img=E6=B5=8F=E8=A7=88?= =?UTF-8?q?=E5=99=A8=E5=81=A5=E5=BA=B7=E6=8E=A2=E6=B5=8B=EF=BC=8C=E5=87=8F?= =?UTF-8?q?=E5=B0=91=E6=88=AA=E5=9B=BE=E5=90=8E=E8=AF=AF=E5=88=A4=E6=96=AD?= =?UTF-8?q?=E8=BF=9E=E9=87=8D=E5=BB=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. _is_browser_alive 增加短重试机制,避免单次瞬时失败被判定为断连\n2. 截图后健康检查由单次探测改为多次探测(超时与间隔可控)\n3. 保留真实断连自动重建能力,但降低误触发概率\n4. 补充详细中文注释,说明探测策略与设计意图 --- utils/markdown_to_image.py | 52 +++++++++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/utils/markdown_to_image.py b/utils/markdown_to_image.py index 5b1de08..1537734 100644 --- a/utils/markdown_to_image.py +++ b/utils/markdown_to_image.py @@ -604,16 +604,44 @@ class _PersistentBrowser: self._ensure_heartbeat_task() return self._browser - async def _is_browser_alive(self, browser, timeout_seconds: float = 3.0) -> bool: - """探测浏览器是否仍可用。""" - if not browser or not browser.is_connected(): - return False - try: - await asyncio.wait_for(browser.version(), timeout=timeout_seconds) - return True - except Exception: + async def _is_browser_alive( + self, + browser, + timeout_seconds: float = 3.0, + retry_count: int = 1, + retry_interval_seconds: float = 0.25, + ) -> bool: + """探测浏览器是否仍可用。 + + 说明: + 1. 单次探测失败可能只是瞬时抖动(例如驱动短暂繁忙); + 2. 因此这里支持短重试,避免把“短暂不可用”误判为“已断连”; + 3. 仅当多次探测都失败时,才认为浏览器真正失活。 + """ + if not browser: return False + probe_times = max(1, int(retry_count or 1)) + timeout = max(float(timeout_seconds or 0), 0.8) + sleep_interval = max(float(retry_interval_seconds or 0), 0.05) + + for idx in range(probe_times): + # 每次探测前都先看连接态,避免对明显失联句柄继续调用 API。 + if not browser.is_connected(): + if idx < probe_times - 1: + await asyncio.sleep(sleep_interval) + continue + return False + try: + await asyncio.wait_for(browser.version(), timeout=timeout) + return True + except Exception: + if idx < probe_times - 1: + await asyncio.sleep(sleep_interval) + continue + return False + return False + async def _heartbeat_loop(self): """周期性探测浏览器可用性,断连后自动重建。""" while True: @@ -677,7 +705,13 @@ class _PersistentBrowser: await _capture_with_browser(browser) # 截图完成后立刻做一次可用性探测。 # 在部分系统环境中,浏览器可能在任务完成后迅速断连,这里主动重建保证“常驻”语义。 - if not await self._is_browser_alive(browser, timeout_seconds=2.0): + # 这里使用“短重试探测”过滤瞬时抖动,避免误判触发不必要重建。 + if not await self._is_browser_alive( + browser, + timeout_seconds=2.5, + retry_count=3, + retry_interval_seconds=0.35, + ): logger.warning("[md2img] 截图后浏览器已断连,立即执行自动重建") await self.restart_browser(reason="post_capture_disconnected") except Exception as e: