如何延迟执行 Web 测试的第一个请求?

Posted

技术标签:

【中文标题】如何延迟执行 Web 测试的第一个请求?【英文标题】:How to delay execution of the first request of a web test? 【发布时间】:2015-09-14 10:36:08 【问题描述】:

在 Visual Studio 2013 Web 性能测试中,如何在我指定的一段时间内延迟发送“.webtest”中的第一个请求?我的网络测试是负载测试的一部分。 Web 测试的第一个请求应在数据源字段指定的延迟时间之后发出。确定何时发出请求很简单:

delayPeriod = dataSourceValue - ( timeNow - loadTestStartTime )

将此delayPeriod 写入请求的思考时间会导致正确的延迟。不幸的是,在收到对请求的响应之后应用思考时间。因此很容易将 web 测试的第二个请求延迟到所需的时间。我想延迟第一个请求之前。

作为一种解决方法,我在http://localhost/ 中包含了一个虚拟的第一个请求,并将预期的状态代码结果设置为 404。所需的延迟设置为该请求的思考时间。但这会向localhost 添加一个不需要的请求。

背景: 我们有一个来自 Web 服务器的日志文件。它给出了每个请求的 URL 和时间。我们希望以与记录请求时相同的速率重复该日志文件中的请求。

【问题讨论】:

【参考方案1】:

好的,再试一次。虽然是简单的,但没有错误检查。

LoadTestStartTime 负载测试插件将对负载测试本身和负载测试开始时间的引用放入 Web 测试上下文中。

public class LoadTestStartTime : ILoadTestPlugin

    public const string LoadTestContextParameterName = "$LoadTest";
    public const string LoadTestStartTimeContextParameterName = "$LoadTestStartTime";

    public void Initialize(LoadTest loadTest)
    
        DateTime startTime = default(DateTime);

        loadTest.LoadTestStarting += (sender, e) =>  startTime = DateTime.Now; ;

        loadTest.TestStarting += (sender,e) => 
            e.TestContextProperties[LoadTestContextParameterName] = loadTest;
            e.TestContextProperties[LoadTestStartTimeContextParameterName] = startTime;
        ;
    

DelayWebTest Web 测试插件必须附加到包含一个虚拟请求的新 Web 测试(不会执行虚拟请求)。这个新的 Web 测试必须作为“初始化测试”添加到负载测试场景测试组合中。

插件从上下文参数中读取延迟时间,然后将场景的当前负载设置为 0。然后它会设置一个计时器,该计时器在正确计算延迟后调用事件处理程序,从而恢复负载配置文件以允许测试继续进行.它使用锁定,因此只有第一个虚拟用户将执行加载配置文件逻辑,而其他用户则等待。

public class DelayWebTest : WebTestPlugin

    public string ContextParameterName  get; set; 

    private bool completed = false;
    private object _lock = new object();

    public override void PreRequest(object sender, PreRequestEventArgs e)
    
        e.Instruction = WebTestExecutionInstruction.Skip;
        if (!completed)
        
            lock (_lock)
            
                if (!completed)
                
                    if (e.WebTest.Context.WebTestUserId > 0) return;
                    LoadTest loadTest = (LoadTest)e.WebTest.Context[LoadTestStartTime.LoadTestContextParameterName];
                    DateTime startTime = (DateTime)e.WebTest.Context[LoadTestStartTime.LoadTestStartTimeContextParameterName];
                    TimeSpan delay = TimeSpan.Parse(e.WebTest.Context[ContextParameterName].ToString());
                    TimeSpan timer = delay - (DateTime.Now - startTime);
                    if (timer > default(TimeSpan))
                    
                        var loadProfile = loadTest.Scenarios[0].LoadProfile.Copy();
                        loadTest.Scenarios[0].CurrentLoad = 0;
                        Timer t = new Timer()  AutoReset = false, Enabled = true, Interval = timer.TotalMilliseconds ;
                        t.Elapsed += (_sender, _e) =>
                        
                            loadTest.Scenarios[0].LoadProfile = loadProfile;
                            t.Stop();
                        ;
                        t.Start();
                    
                    completed = true;
                
            
        
    

似乎有效。 50 个用户,延迟 30 秒:

【讨论】:

问题中的解决方法使我能够进行所需的测试,因此我不再需要花时间在这个主题上,但感谢您的想法。问题在于延迟单个网络测试,而不是延迟场景。 感谢e.Instruction = WebTestExecutionInstruction.Skip;【参考方案2】:

据我所知,您对 localhost 的虚拟请求的解决方案是最好的。但这里有另一种选择。

两部分:

(1) 一个负载测试插件,用于将负载测试开始时间复制到 web 测试上下文中:

[DisplayName("Load Test Start Time")]
[Description("Record the load test start time in the context of new web test iterations")]
public class LoadTestStartTime : ILoadTestPlugin

    public const string LoadTestStartTimeContextParameterName = "$LoadTestStartTime";

    public void Initialize(LoadTest loadTest)
    
        DateTime startTime = DateTime.Now;

        // Copy the load test start time into the context of each new test
        loadTest.TestStarting += (sender, e) =>
        
            e.TestContextProperties[LoadTestStartTimeContextParameterName] = startTime;
        ;
    

(2) 一个 web 测试插件,它从一个可以是静态值或上下文参数的属性中获取延迟时间,可以是 HH:MM:SS.SSS 或整数毫秒:

[DisplayName("Delay WebTest")]
[Description("Delay the start of this WebTest by a certain amount of time.")]
public class DelayWebTest : WebTestPlugin

    [Description("Time to delay this WebTest, in HH:MM:SS.SSS time format or in ms (e.g. 00:01:23.456 or 83456 for 1m 23.456s delay)")]
    public string Delay  get; set; 

    public override void PreWebTest(object sender, PreWebTestEventArgs e)
    
        DateTime start;
        // Make sure this works when not in a load test
        if (!e.WebTest.Context.ContainsKey("$LoadTestUserContext"))
            start = DateTime.Now;
        else
            start = (DateTime)(e.WebTest.Context[LoadTestStartTime.LoadTestStartTimeContextParameterName]);

        TimeSpan elapsed = DateTime.Now - start;
        TimeSpan delay;
        // Support context parameter substitutions
        if (Delay.StartsWith("") && Delay.EndsWith(""))
        
            string key = Delay.Substring(2, Delay.Length - 4);
            object value = e.WebTest.Context[key];
            if (value is TimeSpan) delay = (TimeSpan)value;
            else if (value is int) delay = new TimeSpan(0, 0, 0, 0, (int)value);
            else if (value is string) delay = ParseDelayFromString(value as string);
            else throw new ArgumentException("Delay value must be TimeSpan, int or string.  Found: " + value.GetType());
        
        else delay = ParseDelayFromString(Delay);

        TimeSpan sleep = delay - elapsed;
        if (sleep > new TimeSpan(0))
        
            e.WebTest.AddCommentToResult("Current time: " + DateTime.Now);
            e.WebTest.AddCommentToResult("About to sleep for " + sleep);
            System.Threading.Thread.Sleep(sleep);
            e.WebTest.AddCommentToResult("Current time: " + DateTime.Now);
        
    

    public static TimeSpan ParseDelayFromString(string s)
    
        TimeSpan ts;
        if (TimeSpan.TryParse(s, out ts)) return ts;
        int i;
        if (int.TryParse(s, out i)) return new TimeSpan(0, 0, 0, 0, (int)i);
        throw new FormatException("Could not parse value as TimeSpan or int: " + s);
    

这似乎对我有用。

但是它使用 Thread.Sleep “...不推荐用于 Web 测试是因为它是一种阻塞 API,并且多个 Web 测试可以共享一个线程,因此它会对多个 vuser 产生不利影响。” (See Visual Studio Performance Testing Quick Reference Guide) 第 187 页。

所以这对于单用户负载测试当然是可以的,也许对于虚拟用户数量不超过线程数的小负载*,但它可能会在更大负载时搞砸结果。

(* 我不声称知道线程模型是如何工作的)

【讨论】:

这个答案的主要部分是Thread.Sleep(...) 呼叫,这是我尝试的第一件事。正如您在回答中所说,它会阻塞整个线程,因此只能为极少数虚拟用户工作。请提供 VSPTQRG 中的页码。

以上是关于如何延迟执行 Web 测试的第一个请求?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 ThreadPool 中延迟执行?

在 VSTS Web 测试循环中添加延迟/等待时间 [重复]

如何在不使用睡眠的情况下在 C# 中执行短暂的延迟?

在操场上使用 Alamofire 发出 http 请求的延迟

java中如何是方法延迟执行?

如何延迟android的某段代码执行时间