Pruebas de carga: ¿cómo generar solicitudes por segundo?

14

Tengo un componente de servidor que se ejecuta sobre Zeroc-ICE. Cuando quise cargar la prueba, pensé que usar la biblioteca paralela para crear múltiples solicitudes lo haría. Pero no terminé de esa manera. El uso de la biblioteca Parallel (Parallel.For) de C # fue aparentemente más fácil, pero no parece estar generando exactamente todo en paralelo en el mismo instante. Por lo tanto, no puede ser la definición para crear N solicitudes por segundo. Cómo debería hacerlo ? Supongo que cualquiera que quiera realizar pruebas de carga primero pensaría en esto.

  1. ¿Cuál es la forma eficiente de crear realmente N solicitudes en realidad por segundo?

  2. Otro mito es sobre la programación paralela. Por favor, ilumínenos si ha usado patrones de programación en paralelo en C # o .Net en general. Imagina que tengo 5 procesos. Cómo se iniciarán los cinco procesos al mismo tiempo. ¿Qué significa para mi consumo de recursos? He intentado leer en muchos de los materiales disponibles en la red, pero recibo más y más preguntas que las respuestas a mis preguntas.

  3. Utilicé Parallel.For y creé N hilos y medí el tiempo. Luego intenté lo mismo usando Task.Factory.start para la enumeración de tareas. El tiempo medido fue diferente. Entonces, ¿cuál es exactamente la diferencia entre usar estos? ¿Cuándo debo usar las clases correspondientes y para qué fines exactamente? a menudo tenemos muchas riquezas, pero es que simplemente no sabemos cómo diferenciar una de la otra. Este es uno de esos casos para mí, no poder encontrar por qué no debería usar uno del otro.

  4. Utilicé la clase de cronómetro para medir estos tiempos que dicen ser los mejores. En el escenario de la carga de pruebas de un componente, ¿cuál sería la manera de medir el tiempo de respuesta? El cronómetro parece ser la mejor solución para mí. Cualquier opinión es bienvenida.

ps: hay muchas herramientas de prueba de carga para aplicaciones web. El mío es un caso personalizado de componentes de servidor. Y mi pregunta es más pertinente a la creación de N hilos por segundo.

Todas las opiniones son bienvenidas. Simplemente no pienses que no es tanto una cuestión de programación. Por supuesto que es. Debería sonar las campanas para que cualquier programador que quiera hacer QE sepa por sí mismo el rendimiento de su producto, de primera mano. He probado muchas opciones y luego tuve que recurrir a cómo debería hacerlo.

    
pregunta King 19.01.2012 - 22:27

5 respuestas

10

No tengo todas las respuestas. Ojalá pueda arrojar alguna luz sobre él.

Para simplificar mis declaraciones anteriores sobre los modelos de subprocesos de .NET, simplemente sepa que Parallel Library usa Tareas, y el TaskScheduler predeterminado para Tareas, usa ThreadPool. Cuanto más alto vayas en la jerarquía (ThreadPool está en la parte inferior), más sobrecarga tendrás al crear los elementos. Esa sobrecarga adicional ciertamente no significa que sea más lenta, pero es bueno saber que está ahí. En última instancia, el rendimiento de su algoritmo en un entorno de subprocesos múltiples se reduce a su diseño. Lo que se realiza bien secuencialmente puede no se realiza tan bien en paralelo. Hay demasiados factores involucrados para darle reglas duras y rápidas, cambian según lo que esté tratando de hacer. Ya que estás tratando con solicitudes de red, intentaré y daré un pequeño ejemplo.

Permítame decirle que no soy un experto en enchufes, y no sé casi nada acerca de Zeroc-Ice. Sé algo acerca de las operaciones asíncronas, y aquí es donde realmente te ayudará. Si envía una solicitud sincrónica a través de un socket, cuando llame a Socket.Receive() , su hilo se bloqueará hasta que se reciba una solicitud. Esto no es bueno Tu hilo no puede hacer más solicitudes ya que está bloqueado. Usando Socket.Beginxxxxxx (), la solicitud de E / S se realizará y se pondrá en la cola de IRP para el socket, y su hilo continuará. ¡Esto significa que su hilo podría hacer miles de solicitudes en un bucle sin ningún tipo de bloqueo!

Si lo estoy entendiendo correctamente, está usando llamadas a través de Zeroc-Ice en su código de prueba, en realidad no está tratando de llegar a un punto final de http. Si ese es el caso, puedo admitir que no sé cómo funciona Zeroc-Ice. Sin embargo, sugeriría seguir los consejos enumerados aquí , en particular la parte: Consider Asynchronous Method Invocation (AMI) . La página muestra esto:

  

Al utilizar AMI, el cliente recupera el hilo de control tan pronto como se ha enviado la invocación (o, si no se puede enviar inmediatamente, se ha puesto en cola), lo que permite al cliente utilizar ese hilo para realizar otro trabajo útil en el tiempo medio.

Lo que parece ser el equivalente de lo que describí anteriormente utilizando sockets .NET. Puede haber otras formas de mejorar el rendimiento al intentar hacer muchos envíos, pero me gustaría comenzar aquí o con cualquier otra sugerencia que aparezca en esa página. Ha sido muy vago en cuanto al diseño de su aplicación, por lo que puedo ser más específico de lo que he sido anteriormente. Solo recuerde, no use más subprocesos que absolutamente necesarios para hacer lo que necesita, de lo contrario, es probable que su aplicación se ejecute mucho más lento de lo que desea.

Algunos ejemplos en pseudocódigo (intenté hacerlo lo más cerca posible del hielo sin que yo tuviera que aprenderlo):

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();
}

Una mejor manera:

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);
    }
}

¡Ten en cuenta que hay más subprocesos! = mejor rendimiento al intentar enviar sockets (o realmente hacer cualquier cosa). Los hilos no son mágicos, ya que resolverán automáticamente cualquier problema en el que estés trabajando. Lo ideal es que desee 1 subproceso por núcleo, a menos que un subproceso pase mucho tiempo esperando, entonces puede justificar tener más. Ejecutar cada solicitud en su propio hilo es una mala idea, ya que se producirán cambios de contexto y un desperdicio de recursos. (Si quieres ver todo lo que escribí sobre eso, haz clic en editar y mira las revisiones anteriores de esta publicación. Lo eliminé ya que solo parecía nublar el problema principal en cuestión).

Definitivamente, puede realizar estas solicitudes en subprocesos, si desea realizar un gran número de solicitudes por segundo. Sin embargo, no exageres con la creación de hilos. Encuentra un equilibrio y quédate con él. Obtendrá un mejor rendimiento si utiliza un modelo asíncrono frente a uno sincrónico.

Espero que eso ayude.

    
respondido por el Christopher Currens 19.01.2012 - 23:48
2

Voy a pasar por alto la pregunta 1) y entraré directamente en el # 2, ya que generalmente es una forma aceptable de lograr lo que estás buscando. En el pasado, para lograr n mensajes por segundo, puede crear un solo proceso que luego iniciará p AppDomains. Básicamente, cada dominio de aplicación comienza a ejecutar un bucle de solicitud una vez que se ha alcanzado un cierto punto en el tiempo (usando un temporizador). Este tiempo debe ser el mismo para cada dominio de aplicación para garantizar que comiencen a golpear su servidor en el mismo momento.

Algo como esto debería funcionar para enviar tus solicitudes:

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);
}

Esto probablemente aplastará el rendimiento en la máquina en la que lo esté ejecutando, por lo que siempre podría implementar un tipo similar de bucle desde varias máquinas diferentes si tiene los recursos (utilizando procesos en lugar de dominios de aplicaciones).

Para su tercera pregunta, lea este enlace como enlace

Finalmente, un cronómetro debe emparejarse con un contador de visitas para rastrear tanto la duración como el amp; Golpes únicos en tu servidor. Eso debería permitirle realizar algún análisis después del hecho.

    
respondido por el Chris 19.01.2012 - 23:29
0

No te molestes con los hilos, si N es razonablemente pequeño. Para generar N solicitudes por segundo, use la hora del reloj de pared ( DateTime.Now ). Tómese el tiempo antes y después de la solicitud, luego agregue Sleep para retrasar la próxima solicitud.

Por ejemplo, con N = 5 (200 ms):

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)

Esto no es perfecto; es posible que Sleep no sea exacto. Puede mantener un recuento en curso de las desviaciones (antes de las solicitudes de X'th, el tiempo debe ser X-1 / N más adelante) y ajustar el período de suspensión en consecuencia.

Una vez que N sea demasiado grande, simplemente creas M hilos y dejas que cada hilo genere solicitudes N / M de la misma manera.

    
respondido por el MSalters 20.01.2012 - 12:37
0

La forma más sencilla de realizar pruebas de carga para cualquier proyecto .NET es comprar la edición Ultimate de Visual Studio. Esto viene con herramientas de prueba integradas para ayudar a realizar todo tipo de pruebas, incluidas las pruebas de carga. Las pruebas de carga pueden realizarse previamente creando usuarios virtuales en una sola PC o distribuidos en varios para un mayor número de usuarios, también hay un pequeño programa que se puede instalar en los servidores de destino para devolver datos adicionales durante la prueba.

Sin embargo, esto es costoso, pero la última edición viene con muchas características, por lo que si todas se usaran sería un precio más razonable.

    
respondido por el Ryathal 27.01.2012 - 15:41
0

Si solo quiere que todos los hilos X lleguen a su recurso al mismo tiempo, puede poner cada hilo detrás de un cierre de cuenta atrás y especificar un período de espera corto entre las comprobaciones de semáforo.

C # tiene una implementación (http://msdn.microsoft.com/en-us/library/system.threading.countdownevent(VS.100).aspx).

Al mismo tiempo, si está realizando pruebas de estrés en su sistema, es posible que también desee verificar las condiciones de la carrera, en cuyo caso desearía configurar períodos de suspensión de subprocesos en cada subproceso que oscilan en el tiempo con una frecuencia y picos aleatorizados / permisos.

De manera similar, es posible que no desee enviar solo múltiples solicitudes rápidamente, puede tener un mejor éxito al poner a su servidor en un mal estado / probar su desempeño en el mundo real al configurar una cantidad menor de subprocesos que invierten más tiempo en consumir y enviar mensajes de un lado a otro a través del socket, ya que es probable que su servidor necesite girar sus propios hilos para manejar los mensajes continuos y lentos.

    
respondido por el Keith Brings 01.02.2012 - 22:01

Lea otras preguntas en las etiquetas

Comentarios Recientes

Nuno De La Torre ([email protected]): Esto lleva un poco de tiempo, especialmente para muchas pruebas. Es posible que tengamos muchas velocidades de descarga lentas desde la aplicación, simplemente agregar páginas desde la aplicación y más a nuestro host no es suficiente para medir lo suficientemente bien. P: ¿Cómo se compara esto con otros servicios como D, Shaw y una biblioteca? como Sentry? Rob Duncan (MrDuncan): Diría que todos funcionan, aunque de diferentes maneras. Hay muchas ventajas que no he mencionado,... Lee mas