在并行任务期间跟踪失效的 Web 驱动程序实例

本文关键字:Web 驱动程序 实例 失效 跟踪 并行任务 | 更新日期: 2023-09-27 18:32:12

我看到一些死实例的奇怪现象,使用Selenium WebDriver运行并行嵌套循环Web压力测试,简单的例子是,比如说,点击300个独特的页面,每个页面有100个展示。

我"成功"让 4 - 8 个 WebDriver 实例使用ThreadLocal<FirefoxWebDriver>来隔离每个任务线程,并在 ParallelOptions 实例上使用 MaxDegreeOfParallelism 来限制线程。我只对外部循环(页面集合)进行分区和并行化,并在每个分区的"长时间运行的任务"方法的开头检查ThreadLocal<>容器上的.IsValueCreated。为了便于以后清理,我将每个新实例添加到按线程 ID 键控的 ConcurrentDictionary 中。

无论我使用哪种并行化或分区策略,WebDriver 实例偶尔都会执行以下操作之一:

  • 启动但从不显示网址或展示
  • 启动,运行任意数量的展示,然后在某个时候闲置

当其中任何一个发生时,并行循环最终似乎会注意到线程没有做任何事情,并生成一个新的分区。如果 n 是允许的线程数,则导致只有大约 50-60% 的时间有 n 个生产线程。

清理在

最后仍然工作正常;可能有2n个或更多的打开浏览器,但生产性和非生产性浏览器都会被清理。

有没有办法监控这些无用的 WebDriver 实例并 a) 立即清除它们,以及 b) 让并行循环立即替换任务段,而不是像现在这样滞后几分钟?

在并行任务期间跟踪失效的 Web 驱动程序实例

我遇到了类似的问题。事实证明,WebDriver没有查找开放端口的最佳方法。如此处所述,它会在端口上获取系统范围的锁定,找到一个打开的端口,然后启动实例。这可能会使您尝试启动端口的其他实例匮乏。

我通过在委托中直接为ThreadLocal<IWebDriver>指定一个随机端口号来解决这个问题,如下所示:

        var ports = new List<int>();
        var rand = new Random((int)DateTime.Now.Ticks & 0x0000FFFF);
        var driver = new ThreadLocal<IWebDriver>(() =>
        {
            var profile = new FirefoxProfile();
            var port = rand.Next(50) + 7050;
            while(ports.Contains(port) && ports.Count != 50) port = rand.Next(50) + 7050;
            profile.Port = port;
            ports.Add(port);
            return new FirefoxDriver(profile);
        });

这对我来说非常一致,尽管如果您最终使用列表中未解决的所有 50 个,就会出现问题。

由于没有OnReady事件或IsReady属性,因此我在创建每个实例后通过使线程休眠几秒钟来解决此问题。这样做似乎给了我 100% 持久、正常运行的 WebDriver 实例。

感谢您的建议,我已经在我的开源项目 Webinator 中实现了IsReady功能。如果需要,请使用它,或使用下面概述的代码。

尝试实例化 25 个实例,所有这些实例都是函数式的,所以我在这一点上对算法非常有信心(我利用 HtmlAgilityPack 来查看元素是否存在,但为了简单起见,我将跳过它这里):

public void WaitForReady(IWebDriver driver)
{
    var js = @"{ var temp=document.createElement('div'); temp.id='browserReady';" +
             @"b=document.getElementsByTagName('body')[0]; b.appendChild(temp); }";
    ((IJavaScriptExecutor)driver).ExecuteScript(js);
    WaitForSuccess(() =>
    {
        IWebElement element = null;
        try
        {
            element = driver.FindElement(By.Id("browserReady"));
        }
        catch
        {
            // element not found
        }
        return element != null;
    },
    timeoutInMilliseconds: 10000);
    js = @"{var temp=document.getElementById('browserReady');" +
         @" temp.parentNode.removeChild(temp);}";
    ((IJavaScriptExecutor)driver).ExecuteScript(js);
}
private bool WaitForSuccess(Func<bool> action, int timeoutInMilliseconds)
{
    if (action == null) return false;
    bool success;
    const int PollRate = 250;
    var maxTries = timeoutInMilliseconds / PollRate;
    int tries = 0;
    do
    {
        success = action();
        tries++;
        if (!success && tries <= maxTries)
        {
            Thread.Sleep(PollRate);
        }
    }
    while (!success && tries < maxTries);
    return success;
}

假设如果浏览器正在响应javascript函数并查找元素,那么它可能是一个可靠的实例并可以使用。