Angel "Java" Lopez

NET, Java, PHP y Desarrollo de Software

This Blog

Syndication

Search

Tags

Community

Email Notifications

Archives

.NET

ASP.NET

Windows Form

VB.NET

C#

Sitios

Blogs

Azure: Una aplicación Fractal

En enero pasado, reimplementé mi aplicación Fractal, ahora usando Azure (mis posts sobre Azure). La idea es calcular cada sector de una imagen de un fractal, usando el poder de los worker roles, almacenar el resultado en blobs, y consumirlos desde una aplicación WinForm.

Esta es la solución:

El código está en mi projecto AjCodeKatas, en:

http://code.google.com/p/ajcodekatas/source/browse/#svn/trunk/Azure/AzureFractak

Si quieren, tiene directamente el código congelado a ahora en: AzureFractal.zip.

Los proyectos en la solución:

AzureFractal: la definiciones para Azure (modelo de servicio, configuración).

Fractal: contiene mi código original de mi anterior aplicación fractal. Es un proyecto de librería de clases.

Fractal.Azure: algunos utilitarios de serialización, un servicio fachada para enviar un mensaje a la cola Azure.

AzureLibrary: clases utilitarias que he usado en otros ejemplos Azure. Están evolucionando en cada ejemplo.

FractalWorkerRole: el Worker Role que consumen mensajes de una cola, que indican el sector (un rectángulo) del fractal de Mandelbrot a calcular.

Fractal.GUI: un cliente WinForm que envía y recibe mensajes a/desde colas usadas en el Worker Role.

Deberían configurar la solución para tener dos proyectos a lanzar:

La aplicación WinForm envía un mensaje a una cola, con la información del sector a calcular:

private void Calculate()
{
    Bitmap bitmap = new Bitmap(pcbFractal.Width, 
       pcbFractal.Height);
    pcbFractal.Image = bitmap;
    pcbFractal.Refresh();
    realWidth = realDelta * pcbFractal.Width;
    imgHeight = imgDelta * pcbFractal.Height;
    realMin = realCenter - realWidth / 2;
    imgMin = imgCenter - imgHeight / 2;
    int width = pcbFractal.Width;
    int height = pcbFractal.Height;
    Guid id = Guid.NewGuid();
    SectorInfo sectorinfo = new SectorInfo()
    {
        Id = id,
        FromX = 0,
        FromY = 0,
        Width = width,
        Height = height,
        RealMinimum = realMin,
        ImgMinimum = imgMin,
        Delta = realDelta,
        MaxIterations = colors.Length,
        MaxValue = 4
    };
    Calculator calculator = new Calculator();
    this.queue.AddMessage(
        SectorUtilities.FromSectorInfoToMessage(sectorinfo));
}

El Worker Role lee mensajes desde una cola, y la deserializa en SectorInfo:

while (true)
{
    CloudQueueMessage msg = queue.GetMessage();
    if (msg != null)
    {
        Trace.WriteLine(string.Format("Processing {0}", msg.AsString));
        SectorInfo info = SectorUtilities.FromMessageToSectorInfo(msg);

Si el sector es muy grande, nuevos mensajes son generados:

if (info.Width > 100 || info.Height > 100)
{
    Trace.WriteLine("Splitting message...");
    for (int x = 0; x < info.Width; x += 100)
        for (int y = 0; y < info.Height; y += 100)
        {
            SectorInfo newinfo = info.Clone();
            newinfo.FromX = x + info.FromX;
            newinfo.FromY = y + info.FromY;
            newinfo.Width = Math.Min(100, info.Width - x);
            newinfo.Height = Math.Min(100, info.Height - y);
            CloudQueueMessage newmsg = 
              SectorUtilities.FromSectorInfoToMessage(newinfo);
            queue.AddMessage(newmsg);
        }
}

Si el sector tiene un tamaño tratable, es procesado:

Trace.WriteLine("Processing message...");
Sector sector = calculator.CalculateSector(info);
string blobname = string.Format("{0}.{1}.{2}.{3}.{4}", 
info.Id, sector.FromX, sector.FromY, sector.Width, sector.Height);
CloudBlob blob = blobContainer.GetBlobReference(blobname);
MemoryStream stream = new MemoryStream();
BinaryWriter writer =new BinaryWriter(stream);
foreach (int value in sector.Values)
    writer.Write(value);
writer.Flush();
stream.Seek(0, SeekOrigin.Begin);
blob.UploadFromStream(stream);
stream.Close();
CloudQueueMessage outmsg = new CloudQueueMessage(blobname);
outqueue.AddMessage(outmsg);

Un blob con el resultado es generado, y un mensaje es enviado a otra cola para notificar a la aplicación cliente.

El WinForm tiene un thread con un ciclo leyendo mensajes desde una segunda cola:

string blobname = msg.AsString;
CloudBlob blob = this.blobContainer.GetBlobReference(blobname);
MemoryStream stream = new MemoryStream();
blob.DownloadToStream(stream);
blob.Delete();
this.inqueue.DeleteMessage(msg);
string[] parameters = blobname.Split('.');
Guid id = new Guid(parameters[0]);
int fromx = Int32.Parse(parameters[1]);
int fromy = Int32.Parse(parameters[2]);
int width = Int32.Parse(parameters[3]);
int height = Int32.Parse(parameters[4]);
int[] values = new int[width * height];
stream.Seek(0, SeekOrigin.Begin);
BinaryReader reader = new BinaryReader(stream);
for (int k = 0; k < values.Length; k++)
    values[k] = reader.ReadInt32();
stream.Close();
this.Invoke((Action<int,int,int,int,int[]>) ((x,y,h,w,v) 
=> 
this.DrawValues(x,y,h,w,v)), fromx, fromy, width, height, values);

Notel el uso de .Invoke para ejecutar el dibujo del sector dentro del thread de UI (User Interface).

Esta es la aplicación WinForm, luego de hacer click en el botón Calculate. Noten que los sectores están arrivando:

Hay sectores que todavía no llegaron. Podemos arrastrar el mouse y soltarlo para marcar un nuevo sector:

Podemos cambiar los colores, con el botón New Colors:

Esta es una aplicación de ejemplo, una prueba de concepto. Probablemente, tengamos un mejor rendimiento usando una máquina simple. Pero la idea es que podemos dar trabajo a Worker Roles, especialmente si el trabajo puede hacerse en paralelo (imaginen una máquina para render en paralelo de animaciones). Si Uds. usan esta aplicación en Azure, con varias instancias de Worker Role, el rendimiento podría mejorar.

Próximos pasos: implementar un Web Crawler distribuido, intentar un algoritmo genético distribuido, todo usando la nube Azure.

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Published Wed, Feb 16 2011 11:13 by lopez

Leave a Comment

(required) 
(required) 
(optional)
(required) 
If you can't read this number refresh your screen
Enter the numbers above: