【问题标题】:c# How to load test a webservicec#如何加载测试一个webservice
【发布时间】:2017-02-08 13:13:07
【问题描述】:

我需要测试我们的应用程序中是否存在任何内存泄漏,并监控处理请求时内存使用量是否增加太多。 我正在尝试开发一些代码来同时调用我们的 api/webservice 方法。这个 api 方法不是异步的,需要一些时间来完成它的操作。

我对任务、线程和并行性进行了大量研究,但到目前为止我没有运气。问题是,即使尝试了以下所有解决方案,结果总是相同的,它似乎当时只处理两个请求。

试过了:

-> 在一个简单的 for 循环中创建任务,并使用和不使用 TaskCreationOptions.LongRunning 设置它们来启动它们

-> 在一个简单的 for 循环中创建线程并以高优先级和非高优先级启动它们

-> 在一个简单的 for 循环上创建一个动作列表并使用它们启动它们

Parallel.Foreach(list, options, item => item.Invoke)

-> 直接在 Parallel.For 循环中运行(下)

-> 运行带有和不带有选项和任务调度器的 TPL 方法

-> 尝试使用不同的 MaxParallelism 值和最大线程数

-> 也检查了this post,但也没有用。 (我会错过什么吗?)

-> 在 Stackoverflow 中查看了其他一些帖子,但是对于 F# 解决方案,我不知道如何正确地将它们转换为 C#。 (我从没用过 F#...)

(取自 msdn 的任务计划程序类)

这是我的基本结构:

public class Test
{
    Data _data;
    String _url;

    public Test(Data data, string url)
    {
        _data = data;
        _url = url;
    }

    public ReturnData Execute()
    {
         ReturnData returnData;

         using(var ws = new WebService())
         {
              ws.Url = _url;
              ws.Timeout = 600000;

              var wsReturn = ws.LongRunningMethod(data);

              // Basically convert wsReturn to my method return, with some logic if/else etc
         }
         return returnData;
    }
}

sealed class ThreadTaskScheduler : TaskScheduler, IDisposable
    {
        // The runtime decides how many tasks to create for the given set of iterations, loop options, and scheduler's max concurrency level.
        // Tasks will be queued in this collection
        private BlockingCollection<Task> _tasks = new BlockingCollection<Task>();

        // Maintain an array of threads. (Feel free to bump up _n.)
        private readonly int _n = 100;
        private Thread[] _threads;

        public TwoThreadTaskScheduler()
        {
            _threads = new Thread[_n];

            // Create unstarted threads based on the same inline delegate
            for (int i = 0; i < _n; i++)
            {
                _threads[i] = new Thread(() =>
                {
                    // The following loop blocks until items become available in the blocking collection.
                    // Then one thread is unblocked to consume that item.
                    foreach (var task in _tasks.GetConsumingEnumerable())
                    {
                        TryExecuteTask(task);
                    }
                });

                // Start each thread
                _threads[i].IsBackground = true;
                _threads[i].Start();
            }
        }

        // This method is invoked by the runtime to schedule a task
        protected override void QueueTask(Task task)
        {
            _tasks.Add(task);
        }

        // The runtime will probe if a task can be executed in the current thread.
        // By returning false, we direct all tasks to be queued up.
        protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
        {
            return false;
        }

        public override int MaximumConcurrencyLevel { get { return _n; } }

        protected override IEnumerable<Task> GetScheduledTasks()
        {
            return _tasks.ToArray();
        }

        // Dispose is not thread-safe with other members.
        // It may only be used when no more tasks will be queued
        // to the scheduler.  This implementation will block
        // until all previously queued tasks have completed.
        public void Dispose()
        {
            if (_threads != null)
            {
                _tasks.CompleteAdding();

                for (int i = 0; i < _n; i++)
                {
                    _threads[i].Join();
                    _threads[i] = null;
                }
                _threads = null;
                _tasks.Dispose();
                _tasks = null;
            }
        }
   }

以及测试代码本身:

private void button2_Click(object sender, EventArgs e)
    {
        var maximum = 100;
        var options = new ParallelOptions
        {
             MaxDegreeOfParallelism = 100,
             TaskScheduler = new ThreadTaskScheduler()
        };

        // To prevent UI blocking
        Task.Factory.StartNew(() =>
        {
            Parallel.For(0, maximum, options, i =>
            {
                var data = new Data();
                // Fill data
                var test = new Test(data, _url); //_url is pre-defined
                var ret = test.Execute();

               // Check return and display on screen
               var now = DateTime.Now.ToString("HH:mm:ss");
               var newText = $"{Environment.NewLine}[{now}] - {ret.ReturnId}) {ret.ReturnDescription}";

               AppendTextBox(newText, ref resultTextBox);
           }
     }

    public void AppendTextBox(string value, ref TextBox textBox)
    {
        if (InvokeRequired)
        {
            this.Invoke(new ActionRef<string, TextBox>(AppendTextBox), value, textBox);
            return;
        }
        textBox.Text += value;
    }

而我得到的结果基本上是这样的:

[10:08:56] - (0) OK
[10:08:56] - (0) OK
[10:09:23] - (0) OK
[10:09:23] - (0) OK
[10:09:49] - (0) OK
[10:09:50] - (0) OK
[10:10:15] - (0) OK
[10:10:16] - (0) OK
etc

据我所知,服务器端没有限制。我对并行/多任务世界比较陌生。有没有其他方法可以做到这一点?我错过了什么吗?

(为了清楚起见,我简化了所有代码,并且我相信提供的代码足以描绘所提到的场景。我也没有发布应用程序代码,但它是一个简单的 WinForms 屏幕,只是为了调用和显示结果。如果任何代码都有某种相关性,请告诉我,我也可以编辑和发布。)

提前致谢!

EDIT1:我在服务器日志上检查了它两个接一个地接收请求,所以它确实与发送它们有关,而不是接收。 这可能是与框架如何管理请求/连接有关的网络问题/限制吗?还是与网络有关的东西(与.net无关)?

EDIT2:忘了提,它是一个 SOAP 网络服务。

EDIT3:我发送的属性之一(内部数据)需要针对每个请求进行更改。

EDIT4:我注意到每对请求之间总是有大约 25 秒的间隔,如果相关的话。

【问题讨论】:

  • 您是否尝试过使用Web Test?您可以记录特定请求,然后将此作为VS Load Test 的一部分运行。
  • 谢谢,没试过,看看。但我只需要测试webmethod,没有UI。是否可以使用 Web 测试来实现这一目标?是否可以同时运行很多次?因为我需要请求同时到达服务器,或者最接近服务器。
  • 当然,如果 WebMethod 具有 HTTP 端点,您可以使用 Web 测试记录请求(例如数据 POST)。然后,您可以将负载测试配置为假设 50 个并行用户持续 10 分钟,并具有现实的思考时间。
  • 我同意其他使用工具/服务而不是自己构建的建议。我使用 JMeter,它是开源且高度可配置的,它还支持并行请求。您可以通过 GUI 或无头运行它以满足您的需求。 - jmeter.apache.org
  • 问题是,我们的一些客户基于我们的 web 服务开发了与他们系统的集成,我试图模拟他们通常调用它时所做的事情。我可以使用这些工具实现这一目标吗?因为我不仅需要发送请求,还需要检查它的返回并分析它,因为它有不同的返回码和描述。例如,它可能会返回一些消息,通知业务逻辑失败、成功的代码/消息、异常(例如 OutOfMemoryException)等。

标签: c# web-services parallel-processing task-parallel-library load-testing


【解决方案1】:

我建议不要重新发明轮子,而只使用现有解决方案之一:

  1. 最明显的选择:如果您的 Visual Studio 许可证允许您使用 MS 负载测试框架,那么您很可能甚至不需要编写一行代码:How to: Create a Web Service Test
  2. SoapUI 是一个免费和开源的 Web 服务测试工具,它有一些限制 load testing capabilities
  3. 如果由于某些原因 SoapUI 不适合(即您需要从多个主机以集群模式运行负载测试,或者您需要更多增强的报告),您可以使用 Apache JMeter - 免费和开源的多协议负载测试工具,支持 @ 987654325@ 也是。

【讨论】:

  • 感谢您的回复!我编辑了这个问题,还有一件事:对于每个请求,我需要更改我发送的对象的一个​​属性。我可以使用这些工具实现这一目标吗? (我仍在阅读文档)
  • 所有 3 个选项都是可能的,使用 Visual Studio,您拥有 .NET 框架的所有功能,对于 SoapUI 和 JMeter,您拥有 Groovy language 作为脚本解决方案,在 JMeter 中您也可以配置所有内容使用 GUI(随机化数据或从 CSV 文件、数据库等中获取数据)。
  • 我设法通过在服务器上运行它来使其工作,使用更强大的 CPU。看起来我的计算机无法启动我想要的尽可能多的线程,它按照我当时的预期工作。但这些是一些很好的解决方案,我下次会记住这一点。谢谢!
【解决方案2】:

在不编写自己的项目的情况下创建负载测试的一个很好的解决方案是使用此服务https://loader.io/targets

它对于小型测试是免费的,您可以发布参数、标题等,并且您有一个很好的报告。

【讨论】:

  • 感谢您的回复,但是我们需要做一些比较大的测试,而且我们的测试环境不对外开放。我一直在寻找更灵活/可控的东西,这就是我们尝试制作自己的测试项目的原因。
【解决方案3】:

“一次两个请求”不是连接管理默认的 maxconnection=2 限制的结果吗?

<configuration>  
  <system.net>  
    <connectionManagement>  
      <add address = "http://www.contoso.com" maxconnection = "4" />  
      <add address = "*" maxconnection = "2" />  
    </connectionManagement>  
  </system.net>  
</configuration> 

【讨论】:

    猜你喜欢
    • 2013-10-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-13
    • 1970-01-01
    • 2017-02-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多