负载测试:每秒如何生成请求?


14

我有一个运行在Zeroc-ICE上的服务器组件。当我想对其进行负载测试时,我认为使用并行库创建多个请求即可。但这最终以这种方式结束。从C#使用Parallel(Parallel.For)库显然更容易,但似乎并不能完全在同一瞬间并行生成所有内容。因此,它不是每秒创建N个请求的定义。我该怎么办?我想任何想先进行负载测试的人都会考虑这一点。

  1. 实际每秒实际创建N个请求的有效方法是什么?

  2. 另一个神话是关于并行编程。如果您通常在C#或.Net中使用并行编程模式,请告诉我们。想象一下,我有5个流程。如何将同时启动所有五个过程。这对我的资源消耗意味着什么?我已经尝试阅读了网上提供的许多材料,但是我收到的问题越来越多,而不仅仅是它们作为我的问题的答案。

  3. 我使用Parallel.For并创建了N个线程并测量了时间。然后,我使用Task.Factory.start尝试了同样的事情来枚举任务。测量的时间不同。那么使用它们之间到底有什么不同?什么时候应该使用相应的类?究竟是出于什么目的?我们经常有很多财富,但仅仅是我们不知道如何区分彼此。对我来说就是这种情况,无法找到为什么我不应该使用另一个。

  4. 我使用秒表类来衡量这些时间是最好的。在我对组件进行负载测试的情况下,测量响应时间的方式将是什么。秒表似乎对我来说是最好的解决方案。欢迎任何意见。

ps:有许多针对Web应用程序的负载测试工具。我的是服务器组件的自定义案例。我的问题更多是与每秒创建N个线程有关。

欢迎所有意见。只是不要以为它不是编程问题。当然是。任何想亲自进行QE知识的程序员,首先要亲自了解产品的性能,都应该振作起来。


常见问题解答说,它是否与特定的编程问题有关,以及它在编程界是否是实际可行的问题,可以提出询问。对此表示怀疑并举报的人。请给出意见。
国王

“同一瞬间”是什么意思?我想知道您是否可以通过任何方式强制TPL或PLinq来实现这一目标。
Gert Arnold

我的问题是关于每秒生成N个请求。因此,在这种情况下,同一瞬间意味着我对使用并行将以并行方式启动线程的理解。
国王

您是否进行了任何顺序分析?

3
它可能与编程有关,但是您的帖子中有太多问题(至少4个)。我将它简化为您想要在关闭之前提出的一个问题,因为它太广泛了。提供相关信息,例如您刚才提到的10000(测试计算机中的内核数)。显示代码通常会有所帮助。
Gert Arnold

Answers:


10

我没有所有的答案。希望我能对此有所启发。

为了简化我以前有关.NET线程模型的声明,只需知道Parallel Library使用Tasks,而默认的TaskScheduler for Tasks使用ThreadPool。层次结构越高(线程池位于底部),创建项目时的开销就越大。额外的开销当然并不意味着它会变慢,但是很高兴知道它在那里。最终,算法在多线程环境中的性能取决于其设计。顺序执行良好的结果可能无法并行执行。涉及太多因素,无法为您提供严格的规则,这些规则会根据您要执行的操作而变化。由于您正在处理网络请求,因此我将尝试举一个小例子。

让我指出,我不是套接字专家,而且我对Zeroc-Ice几乎一无所知。我确实了解异步操作,这对您真正有帮助。如果通过套接字发送同步请求,则在调用时Socket.Receive(),线程将阻塞,直到收到请求为止。不好 您的线程无法再发出请求,因为它已被阻止。使用Socket.Beginxxxxxx(),将发出I / O请求并将其放入套接字的IRP队列中,并且您的线程将继续运行。这意味着,您的线程实际上可以在一个循环中发出数千个请求,而没有任何阻塞!

如果我对您的理解正确,则说明您是在测试代码中使用通过Zeroc-Ice进行的调用,而不是实际尝试访问http端点。如果是这样,我可以承认我不知道Zeroc-Ice的工作原理。不过,我建议您遵循此处列出建议,尤其是其中的部分:Consider Asynchronous Method Invocation (AMI)。该页面显示以下内容:

通过使用AMI,客户端可以在发送调用后立即重新获得控制线程(或者,如果无法立即发送,则已经排队),从而允许客户端同时使用该线程执行其他有用的工作。 。

这似乎等效于我上面使用.NET套接字描述的内容。尝试进行大量发送时,可能还有其他提高性能的方法,但是我将从这里开始或从该页面上列出的任何其他建议开始。您对应用程序的设计一直很含糊,所以我可以比上面更具体。请记住,不要使用超出绝对必要数量的线程来完成所需的工作,否则您的应用程序运行速度可能会比所需的慢得多。

伪代码中的一些示例(试图使它尽可能接近冰,而无需我实际学习它):

var iterations = 100000;
for (int i = 0; i < iterations; i++)
{
    // The thread blocks here waiting for the response.
    // That slows down your loop and you're just wasting
    // CPU cycles that could instead be sending/receiving more objects
    MyObjectPrx obj = iceComm.stringToProxy("whateverissupposedtogohere");
    obj.DoStuff();
}

更好的方法:

public interface MyObjectPrx : Ice.ObjectPrx
{
    Ice.AsyncResult GetObject(int obj, Ice.AsyncCallback cb, object cookie);
    // other functions
}

public static void Finished(Ice.AsyncResult result)
{
    MyObjectPrx obj = (MyObjectPrx)result.GetProxy();
    obj.DoStuff();
}

static void Main(string[] args)
{
    // threaded code...
    var iterations = 100000;
    for (int i = 0; i < iterations; i++)
    {
        int num = //whatever
        MyObjectPrx prx = //whatever
        Ice.AsyncCallback cb = new Ice.AsyncCallback(Finished);
        // This function immediately gets called, and the loop continues
        // it doesn't wait for a response, it just continually sends out socket
        // requests as fast as your CPU can handle them.  The response from the
        // server will be handled in the callback function when the request
        // completes.  Hopefully you can see how this is much faster when 
        // sending sockets.  If your server does not use an Async model 
        // like this, however, it's quite possible that your server won't 
        // be able to handle the requests
        prx.GetObject(num, cb, null);
    }
}

请记住,尝试发送套接字(或实际执行任何操作)时,更多的线程!=更好的性能。线程不是魔术,因为它们会自动解决您正在处理的任何问题。理想情况下,您希望每个核心1个线程,除非一个线程花大量时间等待,那么您可以证明拥有更多线程是合理的。在自己的线程中运行每个请求是一个坏主意,因为会发生上下文切换和资源浪费。(如果您想查看我所写的所有内容,请单击“编辑”,然后查看本文的过去修订。我删除了它,因为它似乎笼罩了当前的主要问题。)

如果要每秒发出大量请求,则绝对可以在线程中发出这些请求。但是,不要过度使用线程创建。找到一个平衡并坚持下去。如果使用异步模型而不是同步模型,则将获得更好的性能。

希望对您有所帮助。


您为什么这么在谈论性能?这似乎不是OP想要的。
svick 2012年

1
@svick很好,ops最初的帖子最初有4个问题,他们问有关并行vs任务性能的问题,然后将其编辑掉,现在又放回去了。因此,您所读的大部分内容都是由此而来的。归根结底,尽管他的问题确实与表现有关,但他的总体思路正确,但显然缺乏实施。我相信我最后指出的答案会回答他没有编辑的问题。
Christopher Currens 2012年

1
我被迫减少我的问题,因为他们想投票结束。现在看来,在这里拥有它们是有效的。@ChristopherCurrens +1线程线程与任务的区别是很好的。这扩大了我的理解。但是我仍然坚持认为每秒真的有可能生成N个请求吗?最好的方法到底是什么?
国王

@King-我想我并没有我想的那么清楚。我认为最后3-4段会有所帮助。我以为您已经在使用各种循环了。如果执行此操作,则问题在于套接字的发送/接收被阻塞,从而降低了请求速度。也许我会花些时间来发布一些示例伪代码。
Christopher Currens 2012年

实际上通过ICE发送它们没有问题。问题是什么定义了将实际创建N个请求的实现,以及可以说对那个数字N正确的东西。
国王

2

我将跳过问题1)并直接进入#2,因为通常这是完成您要寻找的内容的一种可接受的方法。过去,要实现每秒n条消息,您可以创建一个进程,然后启动p AppDomains。一旦达到某个时间点(使用计时器),每个AppDomain基本上就开始运行请求循环。每个AppDomain的时间应该相同,以确保它们在同一时间点开始攻击您的服务器。

这样的事情应该可以发送您的请求:

WaitCallback del = state => 
{ 
    ManualResetEvent[] resetEvents = new ManualResetEvent[10000]; 
    WebClient[] clients = new WebClient[10000]; 

    for (int index = 0; index < 10000; index++) 
    { 
        resetEvents[index] = new ManualResetEvent(false); 
        clients[index] = new WebClient(); 

        clients[index].OpenReadCompleted += new OpenReadCompletedEventHandler (client_OpenReadCompleted); 

        clients[index].OpenReadAsync(new Uri(@"<REQUESTURL>"), resetEvents[index]); 
    } 

    bool succeeded = ManualResetEvent.WaitAll(resetEvents, 10000); 
    Complete(succeeded); 

    for (int index = 0; index < 10000; index++) 
    { 
        resetEvents[index].Dispose(); 
        clients[index].Dispose(); 
    } 
}; 

while(running)
{
    ThreadPool.QueueUserWorkItem(del);
    Thread.Sleep(1000);
}

这可能会破坏您在其上运行的任何计算机上的性能,因此,如果您拥有资源(使用进程而不是应用程序域),则始终可以从几台不同的计算机上实现类似类型的循环。

对于您的第三个问题,请给此链接阅读http://www.albahari.com/threading/

最后,秒表应与匹配计数器配对,以跟踪服务器上的持续时间和唯一匹配。那应该让您事后进行一些分析。


2
您必须在这里创建单独的AppDomains的可能原因是什么?这似乎完全没有必要。
2012年

0

如果N很小,请不要理会线程。要每秒生成N个请求,请使用挂钟时间(DateTime.Now)。花费时间在请求之前和之后,然后添加一个Sleep以延迟下一个请求。

例如,N = 5(200毫秒):

Before request: 12:33:05.014
After request: 12:33:05.077
Sleep(137)
Before request: 12:33:05.214
After request: 12:33:05.271
Sleep(131)

这不是完美的。您可能会发现这Sleep是不正确的。您可以保持偏差的连续计数(在第X个请求之前,时间应为X-1 / N之后),并相应地调整“睡眠”时间段。

一旦N变得太大,您只需创建M个线程,然后让每个线程以相同的方式生成N / M个请求。


我必须生成大量请求。因此这不是选择,因为它甚至会在100个线程之前消耗我的内存(4GB RAM)。
2012年

我已经用250K代码从一个线程每秒创建20.000个请求。您没有足够的CPU来运行100个线程(该类机器没有4GB)。下一个问题是将所有这些请求排除在外。您的负载创建者和服务器之间是否有10 Gbit / s以太网?因此,您可能需要检查您的实际需求。
MSalters 2012年

为了澄清,我有大约20 Gbps以上的数据。所以这不是问题。关于机器类别,您要指的是什么?多少处理器?
2012年

@King:要推送100个线程,我希望使用48核机器。例如,SGI销售的机器具有那么多内核,但是在那些内核上,您通常会获得32GB或更多。
MSalters 2012年

0

对任何.NET项目进行负载测试的最简单方法是购买Visual Studio的Ultimate版本。它带有一个集成的测试工具,可以帮助执行包括负载测试在内的各种测试。可以通过在单个PC上创建虚拟用户或在多个PC上为多个用户分布虚拟用户来执行负载测试,还可以在目标服务器上安装一个小程序,以在测试期间返回其他数据。

虽然这很昂贵,但是最终版具有很多功能,因此,如果全部使用它,价格将会更加合理。


0

如果您纯粹想让X个线程在同一时间访问资源,则可以将每个线程放在倒数锁存器后面,并在两次信号量检查之间指定较短的等待时间。

C#具有一个实现(http://msdn.microsoft.com/zh-cn/library/system.threading.countdownevent(VS.100).aspx)。

同时,如果要对系统进行压力测试,则实际上可能还需要检查竞争条件,在这种情况下,您将需要在随时间推移而发生振荡的每个线程上设置线程睡眠时段,并使用随机频率和峰值/谷值。

同样,您可能实际上并不希望只是快速发送多个请求,您可以通过设置较少数量的线程来花费更多时间来消耗和发送消息,从而使服务器进入不良状态/测试其实际性能,从而获得更大的成功。在套接字上来回移动,因为您的服务器可能需要旋转自己的线程来处理缓慢的正在进行的消息。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.