2

我看到一些使用 Selenium WebDriver 运行并行嵌套循环 Web 压力测试的死实例怪异,例如,点击 300 个独特页面,每个页面有 100 次展示。

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

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

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

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

清理最后仍然可以正常工作;可能有 2n 个或更多打开的浏览器,但有效率的和无效率的都会被清理干净。

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

4

2 回答 2

2

我遇到了类似的问题。事实证明,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 个,则会出现问题。

于 2012-07-20T19:23:56.533 回答
1

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

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

我尝试实例化 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 函数并正在查找元素,那么它可能是一个可靠的实例并且可以使用。

于 2012-05-08T16:00:33.127 回答