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

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

delayPeriod = dataSourceValue - ( timeNow - loadTestStartTime )

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

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

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

【问题讨论】:

    标签: visual-studio-2013 load-testing web-testing


    【解决方案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 中的页码。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-14
    • 1970-01-01
    • 2015-05-29
    • 1970-01-01
    • 1970-01-01
    • 2014-11-03
    相关资源
    最近更新 更多