Colabora
 

WebService Asíncrono

Desarrollo de métodos asíncronos en el lado servidor

 

Avatar

Fecha: 27/Nov/2007 (23/11/2007)
Actualizado: 28/Nov/2007
Autor: José Luis Martín - joseluis@phydelta.net

http://www.phydelta.com/blog

Introducción

Durante estas semanas pasadas me he encontrado con el problema de diseñar un sistema escalable que funcione con una interfaz WebService. Aparentemente todo es sencillo si no fuera porque las tareas a realizar en el WS son en algunos casos pesadas y se puede dilatar algunos segundos en el tiempo. Esto provoca que la escalabilidad del sistema se vea comprometida. Tras estudiar el problema uno llega a la conclusión de que una posible solución es ejecutar las tareas de forma asíncrona. Hablamos de ejecución asíncrona de un WS en el lado servidor.

Por otro lado, seguro que todos aquellos programadores que habitualmente trabajéis con WebServices . Estaréis familiarizados con las clases proxy que genera el propio Visual Studio. Si os fijáis un poco en la clase proxy que os genera veréis como os ha generado para cada WebMethod dos formas diferentes de llamarlo, una de forma síncrona y otra de forma asíncrona. De este modo se soluciona el que la aplicación cliente quede bloqueada a la espera de que un WS termine. Pero no hay nada sobre cómo se ejecuta en el lado servidor.
Pero, para el problema que nos ocupa, es necesario que el WebMethod se ejecute de forma asíncrona. A continuación veremos Cómo podemos conseguirlo.

Reproducir el problema.

Generamos un WS con el siguiente WebMethod

[WebMethod]
public int SumaSincrono(int a, int b)
{
System.Threading.Thread.Sleep(3000);
return a + b ; 
}

¡¡¡¡Simple!!!

Si lo ejecutamos…. A los 3 segundos obtenemos una respuesta con el resultado de la suma. El problema esta cuando simulamos 200 usuarios simultáneos. ¡¡Sorpresa!!

Test WS síncrono

Se puede observar que tras estabilizarse cada vez que se llama al webmethod este tiene una duración de unos 12 segundos.

Fundamento Teórico

Cuando nosotros ejecutamos una página ASP.NET esta se ejecuta consumiendo un thread del Pool reservado a ASP.NET. Hay que saber que aunque el CLR reserva 100 thread por CPU para ASP.NET, este únicamente hace uso de 12 thread por CPU.

En el ejemplo cada llamada tiene "ocupado" un thread durante 3 segundos, por lo que se hace muy complicado soportar 200 usuarios simultáneos. ¡Por cierto! Mi maquina tiene un CoreDuo por lo que tenemos 24 Thread disponibles.

La solución

Existen un montón de documentación referentes a las diferentes técnicas de realizar paginas aspx asíncronas, pero apenas se encuentra documentación referente a como realizar un webservice asíncrono en el lado servidor.

http://msdn2.microsoft.com/en-US/library/aa480516.aspx

Escribimos el siguiente código.

public delegate int SumaAsincronoDelegado (int a, int b);

public class MyState
    {
    public object EstadoPrevio;
    public SumaAsincronoDelegado dlg;
    }

    [WebMethod()]
    public IAsyncResult BeginSumaAsincrono(int a, int b, AsyncCallback cb, object s)
    {
    SumaAsincronoDelegado dlg = new SumaAsincronoDelegado(SumaAsincrono);
    MyState ms = new MyState();
    ms.dlg = dlg;
    ms.EstadoPrevio = s;

    return dlg.BeginInvoke(a, b, cb, ms);
    }

    [WebMethod]
    public int EndSumaAsincrono(IAsyncResult call)
    {
    MyState ms = (MyState)call.AsyncState;
    return ms.dlg.EndInvoke(call);
    }

    private int SumaAsincrono(int a, int b)
    {
    System.Threading.Thread.Sleep(3000);
    return a + b;
    }

Ahora el método "SumaAsincrono" se ha desdoblado en "BeginSumaAsincrono" y "EndSumaAsincrono" De forma genérica podemos decir que todo método lo podemos hacer asíncrono simplemente añadiéndole los prefijos Beginxxx y Endxxx. Y que cumplan con las firmas :

[WebMethod]
public IAsyncResult Beginxxx([parametros de entrada], AsyncCallback cb, object s)

[WebMethod]
public [Tipo] Endxxx(IAsyncResult call)

¿Cómo se ve el método desde un cliente? Pues igual que si lo hiciéramos de forma síncrona, es decir aparece un método "xxx" Para el cliente es trasparente el si ejecutamos el método de forma síncrona o asíncrona en el lado servidor.

¡¡Ahora llega la hora de la verdad!! Para dar fe de lo que os estoy contando, he realizado un test para verificar la mejora en la escalabilidad.

Test WS síncrono

Tras estabilizarse el servidor cada petición tiene una duración de unos 3,2 segundos. Apenas una par de decimas de retraso y soportando 200 usuarios.

¿Qué ha ocurrido?

Ahora cada petición se ejecuta en un thread distinto unos de esos 88 thread por cpu que no utiliza asp.net, dejando los 12 thread/cpu libres para admitir nuevas peticiones.

Con este artículo he tratado de ilustrar como realizar WebServices escalables

Un saludo.

José Luis Martín.
joseluis@phydelta.net
http://www.phydelta.com/blog



Código de ejemplo (comprimido):

 

Fichero con el código de ejemplo: phydelta_ws_asincrono_codigo.zip - 18.8 KB

(MD5 checksum: 2E940E8AAF69FF0E407980ED9C7427AC)

 


Ir al índice principal de el Guille