2

Selenium WebDriver を使用して並列化された入れ子になったループの Web ストレス テストを実行すると、デッド インスタンスの奇妙さがいくつか見られます。単純な例として、それぞれ 100 インプレッションで 300 の一意のページにヒットしたとします。

を使用して 4 ~ 8 個の WebDriver インスタンスを「正常に」取得して、ThreadLocal<FirefoxWebDriver>タスク スレッドごとにそれらを分離し、ParallelOptions インスタンスで MaxDegreeOfParallelism を使用してスレッドを制限しています。外側のループ (ページのコレクション) のみを分割して並列化し、各パーティションの「長時間実行タスク」メソッドの先頭にあるコンテナーをチェック.IsValueCreatedしています。ThreadLocal<>後でクリーンアップを容易にするために、スレッド ID をキーとする ConcurrentDictionary に新しいインスタンスをそれぞれ追加します。

私が使用する並列化または分割戦略に関係なく、WebDriver インスタンスは次のいずれかを実行することがあります。

  • 起動するが、URL を表示したり、インプレッションを実行したりしない
  • ローンチし、インプレッションを何回でも実行してから、ある時点でアイドル状態にします

これらのいずれかが発生すると、並列ループは最終的にスレッドが何もしていないことに気づき、新しいパーティションを生成します。nが許容されるスレッドの数である場合、n 個の生産的なスレッドは時間の約 50 ~ 60% しか持たないという結果になります

クリーンアップは最後でも問題なく機能します。2n 以上のブラウザーが開いている可能性がありますが、生産的なブラウザーも非生産的なブラウザーも同様にクリーンアップされます。

これらの役に立たない WebDriver インスタンスを監視し、a) それらをすぐに清掃し、さらに b) 現在のように数分間遅れる代わりに、並列ループを取得してタスク セグメントをすぐに置き換える方法はありますか?

4

2 に答える 2

2

私は同様の問題を抱えていました。WebDriver には、開いているポートを見つける最適な方法がないことがわかりました。hereで説明されているように、ポートでシステム全体のロックを取得し、開いているポートを見つけて、インスタンスを起動します。これにより、ポートを開始しようとしている他のインスタンスが不足する可能性があります。

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 に答える