我需要测试我们的应用程序和监视器中是否存在内存泄漏,以查看在处理请求时内存使用量是否增加过多。我正在尝试开发一些代码,以便对我们的api/webservice方法进行多次同时调用。这个api方法不是异步的,并且需要一些时间来完成它的操作。
我对任务、线程和并行性做了很多研究,但到目前为止我运气不佳。问题是,即使尝试了以下所有解决方案,结果总是一样的,似乎一次只处理两个请求。
尝试:
->在简单的for循环中创建任务,并使用TaskCreationOptions.LongRunning 启动和不设置任务
->在一个简单的for循环中创建线程,并使用和不使用高优先级启动它们
->在一个简单的for循环上创建一个操作列表,并使用启动它们
Parallel.Foreach(list, options, item => item.Invoke)
->直接在Parallel内部运行。对于循环(下面)
->使用和不使用Options和TaskScheduler 运行TPL方法
->尝试使用不同的MaxParallelism和最大线程值
->也检查了这篇文章,但也没有帮助。(我会错过什么吗?)
->在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屏幕,只是用来调用和显示结果。如果任何代码在某种程度上相关,请告诉我,我也可以编辑和发布它。)
提前感谢!
第1版:我在服务器日志上检查了它是两个接一个地接收请求的,所以这确实与发送请求有关,而不是接收请求。这可能是与框架如何管理请求/连接有关的网络问题/限制吗?或者网络上有什么东西(与.net无关)?
编辑2:别提了,这是一个SOAP Web服务。
编辑3:我发送的一个属性(内部数据)需要为每个请求更改。
第4版:我注意到,如果相关的话,每对请求之间总是有大约25秒的间隔。
我建议不要重新发明轮子,只使用现有的解决方案之一:
- 最明显的选择:如果您的Visual Studio许可证允许您使用MS负载测试框架,那么很可能您甚至不需要写一行代码:如何:创建Web服务测试
- SoapUI是一个免费的开源web服务测试工具,它具有一些有限的负载测试功能
- 如果由于某些原因SoapUI不合适(即,您需要从多个主机以集群模式运行负载测试,或者您需要更多增强的报告),您可以使用ApacheJMeter-免费的开源多协议负载测试工具,该工具也支持web服务负载测试
创建负载测试而不编写自己的项目的一个好的解决方案是使用此服务https://loader.io/targets
它对小型测试是免费的,您可以POST参数、Header,。。。你的报道也不错。
我最喜欢的负载测试库是NBomber。它有一个简单而强大的API,逼真的用户模拟,并为您提供关于延迟和每秒请求的漂亮HTML报告。我用它来测试我的API,并写了一篇关于我是如何做到这一点的文章
"一次两个请求"是否是connectionManagement默认maxconnection=2限制的结果?
<configuration>
<system.net>
<connectionManagement>
<add address = "http://www.contoso.com" maxconnection = "4" />
<add address = "*" maxconnection = "2" />
</connectionManagement>
</system.net>
</configuration>