mirror of
https://git.oceanpay.cc/danial/kami_apple_exchage.git
synced 2025-12-18 21:23:49 +00:00
refactor(playwright_manager.py): 优化Playwright管理器代码
- 移除了未使用的`sys`导入。 - 改进了`headless`选项值的处理逻辑,使其更易读。 - 增强了日志记录信息的格式,使其更加清晰。 - 在创建浏览器上下文时增加了重试机制,并使用配置的超时时间。 feat(docker-entrypoint.sh): 添加对beat和flower服务类型的支持 - 当`SERVICE_TYPE`为`beat`时,启动Celery Beat服务。 - 当`SERVICE_TYPE`为`flower`时,启动Celery Flower监控服务。 - 更新了错误提示信息,以反映新的服务类型选项。 test(test_playwright_manager.py): 添加Playwright管理器的单元测试 - 新增了一个测试类`TestDistributedPlaywrightManager`。 - 包含一个测试方法`test_create_context`,用于验证上下文创建功能。
This commit is contained in:
@@ -7,7 +7,6 @@ Playwright分布式管理器
|
||||
import asyncio
|
||||
import os
|
||||
import platform
|
||||
import sys
|
||||
import time
|
||||
import uuid
|
||||
from contextlib import asynccontextmanager
|
||||
@@ -156,7 +155,12 @@ class DistributedPlaywrightManager:
|
||||
if "headless" in launch_options:
|
||||
headless_value = launch_options["headless"]
|
||||
if isinstance(headless_value, str):
|
||||
launch_options["headless"] = headless_value.lower() in ("true", "1", "yes", "on")
|
||||
launch_options["headless"] = headless_value.lower() in (
|
||||
"true",
|
||||
"1",
|
||||
"yes",
|
||||
"on",
|
||||
)
|
||||
elif not isinstance(headless_value, bool):
|
||||
launch_options["headless"] = bool(headless_value)
|
||||
|
||||
@@ -164,7 +168,7 @@ class DistributedPlaywrightManager:
|
||||
|
||||
# 启动Chromium浏览器
|
||||
cls._browser = await cls._playwright.chromium.launch(**launch_options)
|
||||
|
||||
|
||||
# 检查浏览器是否成功启动并连接
|
||||
if not cls._browser.is_connected():
|
||||
logger.error("浏览器启动后未连接")
|
||||
@@ -184,7 +188,12 @@ class DistributedPlaywrightManager:
|
||||
|
||||
except Exception as e:
|
||||
worker_id = cls._instance._worker_id if cls._instance else "unknown"
|
||||
logger.error("初始化Playwright失败", error=str(e), worker_id=worker_id, exc_info=True)
|
||||
logger.error(
|
||||
"初始化Playwright失败",
|
||||
error=str(e),
|
||||
worker_id=worker_id,
|
||||
exc_info=True,
|
||||
)
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
@@ -290,7 +299,7 @@ class DistributedPlaywrightManager:
|
||||
try:
|
||||
if not cls._browser:
|
||||
raise RuntimeError("Browser实例不可用")
|
||||
|
||||
|
||||
# 检查浏览器是否已连接
|
||||
if not cls._browser.is_connected():
|
||||
logger.warning("浏览器未连接,尝试重新初始化")
|
||||
@@ -301,22 +310,56 @@ class DistributedPlaywrightManager:
|
||||
raise RuntimeError("浏览器重新连接失败")
|
||||
|
||||
logger.info("创建新上下文")
|
||||
# 为new_context调用添加超时控制
|
||||
try:
|
||||
context = await asyncio.wait_for(
|
||||
cls._browser.new_context(),
|
||||
timeout=30.0 # 30秒超时
|
||||
)
|
||||
except asyncio.TimeoutError:
|
||||
logger.error("创建浏览器上下文超时")
|
||||
raise RuntimeError("创建浏览器上下文超时")
|
||||
|
||||
|
||||
# 使用配置的超时时间,增加重试逻辑
|
||||
max_retries = 3
|
||||
retry_delay = 2.0 # 初始重试延迟秒数
|
||||
|
||||
for attempt in range(max_retries):
|
||||
try:
|
||||
# 使用配置的Playwright超时时间
|
||||
context = await asyncio.wait_for(
|
||||
cls._browser.new_context(**context_options),
|
||||
timeout=settings.PLAYWRIGHT_TIMEOUT,
|
||||
)
|
||||
break # 成功创建上下文,退出重试循环
|
||||
|
||||
except asyncio.TimeoutError:
|
||||
if attempt == max_retries - 1:
|
||||
logger.error("创建浏览器上下文超时,已达到最大重试次数")
|
||||
raise RuntimeError("创建浏览器上下文超时")
|
||||
|
||||
logger.warning(
|
||||
"创建浏览器上下文超时,准备重试",
|
||||
attempt=attempt + 1,
|
||||
max_retries=max_retries,
|
||||
timeout=settings.PLAYWRIGHT_TIMEOUT,
|
||||
retry_delay=retry_delay,
|
||||
)
|
||||
await asyncio.sleep(retry_delay)
|
||||
retry_delay *= 2 # 指数退避
|
||||
|
||||
except Exception as e:
|
||||
if attempt == max_retries - 1:
|
||||
logger.error("创建浏览器上下文失败", error=str(e))
|
||||
raise RuntimeError(f"创建浏览器上下文失败: {str(e)}")
|
||||
|
||||
logger.warning(
|
||||
"创建浏览器上下文失败,准备重试",
|
||||
error=str(e),
|
||||
attempt=attempt + 1,
|
||||
max_retries=max_retries,
|
||||
retry_delay=retry_delay,
|
||||
)
|
||||
await asyncio.sleep(retry_delay)
|
||||
retry_delay *= 2 # 指数退避
|
||||
|
||||
logger.info("设置默认超时")
|
||||
|
||||
# 设置默认超时
|
||||
context.set_default_timeout(60000)
|
||||
context.set_default_navigation_timeout(30000)
|
||||
|
||||
|
||||
logger.info("应用上下文选项")
|
||||
|
||||
cls._contexts[context_id] = context
|
||||
|
||||
@@ -58,7 +58,17 @@ elif [ "$SERVICE_TYPE" = "worker" ]; then
|
||||
--without-gossip \
|
||||
--without-mingle \
|
||||
--without-heartbeat
|
||||
elif [ "$SERVICE_TYPE" = "beat" ]; then
|
||||
exec celery -A app.core.celery_app beat \
|
||||
--loglevel=info \
|
||||
--schedule=/tmp/celerybeat-schedule \
|
||||
--pidfile=/tmp/celerybeat.pid
|
||||
elif [ "$SERVICE_TYPE" = "flower" ]; then
|
||||
exec celery -A app.core.celery_app flower \
|
||||
--port=5555 \
|
||||
--host=0.0.0.0 \
|
||||
--loglevel=info
|
||||
else
|
||||
echo "SERVICE_TYPE must be 'api' or 'worker'"
|
||||
echo "SERVICE_TYPE must be 'api', 'worker', 'beat', or 'flower'"
|
||||
exit 1
|
||||
fi
|
||||
13
backend/tests/test_playwright_manager.py
Normal file
13
backend/tests/test_playwright_manager.py
Normal file
@@ -0,0 +1,13 @@
|
||||
import uuid
|
||||
from unittest import IsolatedAsyncioTestCase
|
||||
|
||||
from app.core.playwright_manager import playwright_manager
|
||||
|
||||
|
||||
class TestDistributedPlaywrightManager(IsolatedAsyncioTestCase):
|
||||
async def test_create_context(self):
|
||||
# 使用Playwright上下文管理器
|
||||
async with playwright_manager.get_order_context(
|
||||
uuid.uuid4().hex
|
||||
) as context_info:
|
||||
print("我成功创建了一个上下文")
|
||||
Reference in New Issue
Block a user