在并行任务期间跟踪死WebDriver实例

我看到一些使用Selenium WebDriver运行并行化嵌套循环Web压力测试的死实例怪异,简单的例子就是说,打出300个独特的页面,每个页面有100个印象。

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

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

  • 启动但从不显示url或投放展示
  • 启动,运行任意数量的印象,然后在某些时候闲置

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

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

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

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

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

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

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

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

  var ports = new List(); var rand = new Random((int)DateTime.Now.Ticks & 0x0000FFFF); var driver = new ThreadLocal(() => { 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个,则会出现问题。