【发布时间】: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