You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

discord.py 2.0机器人如何并发执行多个Selenium测试

问题根因
  • 同步逻辑阻塞异步事件循环:discord.py 2.0的斜杠命令回调运行在bot主异步事件循环中,Selenium本身为同步阻塞实现,直接在回调中执行会卡住整个事件循环,导致后续请求无法被正常调度,触发交互超时、上下文失效类报错。
  • 浏览器实例无隔离且资源未释放:现有代码执行完测试逻辑后未调用driver.quit()回收资源,残留的chromedriver、Chrome进程会持续占用端口与系统资源;同时Chrome默认启动时会复用全局临时用户数据目录,多实例同时启动时会触发文件锁冲突,即截图中呈现的报错。
  • 无并发调度设计:所有Selenium任务默认在事件循环所在线程串行执行,不具备并行处理能力。
修复方案

按以下步骤调整代码即可支持多任务并行:

1. 配置Chrome启动参数实现实例隔离

Debian服务器无桌面环境,且多实例启动Chrome需要独立的用户数据目录避免锁冲突,首先补充对应依赖导入:

from selenium.webdriver.chrome.options import Options
import tempfile
import asyncio
from functools import partial

然后封装独立的driver初始化方法:

def init_driver():
    chrome_options = Options()
    # 无头模式适配服务器无桌面环境
    chrome_options.add_argument("--headless=new")
    chrome_options.add_argument("--no-sandbox")
    chrome_options.add_argument("--disable-dev-shm-usage")
    # 为每个实例分配独立临时用户目录,彻底避免文件锁冲突
    chrome_options.add_argument(f"--user-data-dir={tempfile.mkdtemp()}")
    # 精简非必要功能降低资源占用
    chrome_options.add_argument("--disable-gpu")
    chrome_options.add_argument("--disable-extensions")
    driver = webdriver.Chrome(
        executable_path=ChromeDriverManager().install(),
        options=chrome_options
    )
    driver.maximize_window()
    return driver

2. 封装同步Selenium任务,避免阻塞主事件循环

所有Selenium同步逻辑放到独立的同步函数中执行,通过异步事件循环的线程池调度器运行,不占用主事件循环线程;同时增加资源回收逻辑,必须保证无论任务执行成功或失败,都调用driver.quit()释放实例:

def sync_selenium_task(url):
    driver = None
    try:
        driver = init_driver()
        driver.get(url)
        # 此处替换为原selenium_py函数中的所有测试逻辑
        page_title = driver.title
        return f"测试完成,目标页面标题:{page_title}"
    except Exception as e:
        return f"测试执行失败:{str(e)}"
    finally:
        if driver:
            driver.quit()

async def selenium_py(url):
    loop = asyncio.get_running_loop()
    # 将同步任务提交到线程池执行,支持多任务并行调度
    result = await loop.run_in_executor(None, partial(sync_selenium_task, url))
    return result

3. 调整斜杠命令回调逻辑,适配长任务响应规则

Discord要求斜杠交互必须在3秒内返回响应,否则会判定交互失效,因此需要先发送延迟响应,等测试执行完成后再推送最终结果:

@tree.command(name="selenium", description="run a selenium test", guild=discord.Object(id=729380155734228994))
async def selenium_command(interaction: discord.Interaction, url: str):
    # 发送延迟响应,规避3秒交互超时限制
    await interaction.response.defer(ephemeral=True)
    try:
        test_result = await selenium_py(url)
        await interaction.followup.send(f"Successfully ran a selenium test\n{test_result}", ephemeral=True)
    except Exception as e:
        await interaction.followup.send(f"Test failed: {str(e)}", ephemeral=True)

4. (可选)配置最大并发数限制

如果服务器配置有限,可以通过异步信号量限制同时运行的Selenium任务上限,避免资源耗尽:

# 根据服务器配置调整最大并发数,例如最多同时运行3个测试任务
MAX_CONCURRENT_SELENIUM_TASKS = 3
selenium_task_semaphore = asyncio.Semaphore(MAX_CONCURRENT_SELENIUM_TASKS)

async def selenium_py(url):
    async with selenium_task_semaphore:
        loop = asyncio.get_running_loop()
        result = await loop.run_in_executor(None, partial(sync_selenium_task, url))
        return result
部署注意事项
  • Debian服务器需要提前安装Chrome/Chromium及系统依赖,否则Chrome实例无法启动,执行以下命令安装:
apt update && apt install -y chromium-browser chromium-chromedriver
  • 禁止将webdriver.Chrome实例设为全局共享变量,必须遵循一个测试任务对应一个独立driver实例、任务结束立即销毁的原则,否则无法实现并发。
  • 如果测试逻辑资源占用极高,可以将线程池替换为进程池获得更好的隔离性,常规测试场景使用默认线程池即可满足需求。

内容的提问来源于stack exchange,提问作者Maxisy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 19:15:39