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

February 2011 - Posts

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

Posted Wed, Feb 16 2011 11:13 by lopez | with no comments

Azure: Una aplicación simple usando Tables

Continuando con mis ejemplos de Azure, esta vez escribí una simple aplicación web CRUD, usando Tables, con Tables, usando Azure Storage Client.

Es una aplicación de ASP.NET clásico, con esta vista para CustomerList.aspx:

Pueden bajarse la solución desde mi AjCodeKatas Google project. El código está en:

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

Si quieren pueden bajarse directamente la versión “congelada” desde: AzureCustomers.zip.

La simple entidad Customer:

public class Customer : TableServiceEntity
{
    public Customer()
        : this(Guid.NewGuid().ToString())
    {
    }
    public Customer(string id)
        : base(id, string.Empty)
    {
    }
    public string Name { get; set; }
    public string Address { get; set; }
    public string Notes { get; set; }
}

Estoy usando la PartitionKey como clave primaria, llenándola con un Guid. La RowKey es el string vacío. En una aplicación menos simple, podría grabar las facturas de un cliente usando la misma Partition Key, identificando cada factura con un RowKey distinto.

Un DataContext está a cargo de exponer un IQueryable de Customers:

public class DataContext : TableServiceContext
{
    public const string CustomerTableName = "Customers";
    public DataContext(string baseAddress, StorageCredentials credentials)
        : base(baseAddress, credentials)
    {
        this.IgnoreResourceNotFoundException = true;
    }
    public DataContext(CloudStorageAccount storageAccount)
        : base(storageAccount.TableEndpoint.AbsoluteUri, storageAccount.Credentials)
    {
        this.IgnoreResourceNotFoundException = true;
    }       
    public IQueryable<Customer> Customers
    {
        get
        {
            return this.CreateQuery<Customer>(CustomerTableName);
        }
    }
}

Noten el IgnoreNotFoundException: si está en verdadero, puedo recuperar un cliente inexistente sin levantar una excepción, y retornar un valor null.

Hay un servicio para acceder y manejar los Customers:

public class CustomerServices
{
    private DataContext context;
    public CustomerServices(DataContext context)
    {
        this.context = context;
    }
    public Customer GetCustomerById(string id)
    {
        return this.context.Customers.Where(c => c.PartitionKey == id).SingleOrDefault();
    }
    public IEnumerable<Customer> GetCustomerList()
    {
        return this.context.Customers.ToList().OrderBy(c => c.Name);
    }
    public void AddCustomer(Customer customer)
    {
        this.context.AddObject(DataContext.CustomerTableName, customer);
        this.context.SaveChanges();
    }
    public void UpdateCustomer(Customer customer)
    {
        this.context.AttachTo(DataContext.CustomerTableName, customer, "*");
        this.context.UpdateObject(customer);
        this.context.SaveChanges();
    }
    public void DeleteCustomerById(string id)
    {
        Customer c = this.GetCustomerById(id);
        this.context.DeleteObject(c);
        this.context.SaveChanges();
    }
}

Noten el uso de Attach con ETag (tercer parámetro) de “*” (any). De esta forma, podemos actualizar el cliente adosando el “creado en memoria” al contexto de datos, sin recuperalo desde la base de datos. Esta forma de hacerlo es viable si tengo todos los datos del cliente, y no me importa sobreescribir cambios posibles que se hubieran hecho en el storage. En muchas aplicaciones uno cambia solamente parte del estado, ante una operación (por ejemplo, cambio de domiciolio). Entonces se recupera el objeto, se cambia parte del estado y se lo graba.

Usando el servico para recuperar los clientes:

CloudStorageAccount storage = CloudStorageAccount.FromConfigurationSetting("DataConnectionString");
CustomerServices services = new CustomerServices(new DataContext(storage));
this.grdCustomerList.DataSource = services.GetCustomerList();
this.grdCustomerList.DataBind();

Nota: ésta es una aplicación de ejemplo, simple y directa. Una aplicación real debería separar el modelo de vista del modelo de negocio, y posiblemente, usar ASP.NET MVC en la presentación. Escribiré este ejemplo usando MVC. En otra serie (fuera de ésta relacionado con Azura), quiero escribir una aplicación ASP.NET MVC usando TDD.

Próximos pasos en Azure: una aplicación distribuida que calcule un fractar, un web crawler distribuido, un algoritmo genético usando Worker Roles.

Nos leemos!

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

Posted Wed, Feb 9 2011 9:50 by lopez | with no comments

Un Servidor Http Mínimo en C#

Hace dos meses, escribí un post implementando un servidor HTTP mínimo en Java:

Un Servidor Http Mínimo en Java

En estos días, comencé a explorar node.js. Quiero implementar un servidor mínimo en AjSharp que mapee pedidos entrantes a funciones dinámincas, a la node.js/javascript. Pero antes de eso, quiero una implementación pura y mínima en C#. Podría usar TcpListener (similar al ServerSocket de Java) , como en el proyecto del 2001:

Create your own Web Server using C#

Pero en .NET, tenemos un web listener mínimo en la clase System.Net.HttpListener. Ver:

HttpListener for dummies: a simple “HTTP Request” reflector

Así, que usando esa clase, escribí un código de consola, mínimo:

class Program
{
    static string rootDirectory;
    static void Main(string[] args)
    {
        rootDirectory = args[0];
        HttpListener listener = new HttpListener();
        for (int k = 1; k < args.Length; k++)
            listener.Prefixes.Add(args[k]);
        listener.Start();
        while (true)
        {
            HttpListenerContext context = listener.GetContext();
            Process(context);
        }
    }
    private static void Process(HttpListenerContext context)
    {
        string filename = context.Request.Url.AbsolutePath;
        Console.WriteLine(filename);
        filename = filename.Substring(1);
        if (string.IsNullOrEmpty(filename))
            filename = "index.html";
        filename = Path.Combine(rootDirectory, filename);
        Stream input = new FileStream(filename, FileMode.Open);
        byte[] buffer = new byte[1024*16];
        int nbytes;
        while ((nbytes = input.Read(buffer, 0, buffer.Length)) > 0)
            context.Response.OutputStream.Write(buffer, 0, nbytes);
        input.Close();
        context.Response.OutputStream.Close();
    }
}

El web server retorna el contenido de archivos estáticos, desde un directorio raíz. El primer parámetro es el directorio raíz, y el resto de los parámetros son los patrones a escuchar:

El resultado, usando archivos estáticos que tengo en directorio de documentación de Tomcat:

La salida por consola:

Próximos pasos: usar este código en AjSharp. O extenderlo para soportar un pool de threads, o atender llamadas como /run/<comandoaejecutar> en el servidor.

Nos leemos!

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

Posted Sat, Feb 5 2011 19:16 by lopez | with no comments

Filed under: ,

Jugando con Node.js, Ubuntu, Sqlite3 y node-Sqlite

Hace unos días, estuve jugando con Node.Js, la librería Javascript que puede ser usada para crear aplicaciones web en el servidor. Como puede ser complicado instalarlo en Windows, lo instalé directamente en Ubuntu. Me bajé el código de:

http://nodejs.org/#download

Expandí el archivo, y pasé al directorio que quedó creado. Ahí ejecuté los pasos:

./configure
make
sudo make install

(Noten que es diferente de lo que indica una de mis fuentes consultadas Learning Server-Side Javascript with Node.js: agregué el .configure, nacido hace décadas debido a la necesidad de detectar en qué sistema se va a ejecutar el make; recuerdo aquellos tiempos donde sólo había make ;-)

Hecho esto, instalé Sqlite3 en mi Ubuntu (no Sqlite, sino la versión 3):

sudo apt-get install sqlite3

En un directorio de trabajo para este proyecto, ejecuté:

sqlite3 test.db

Esta es una línea de comando (no hay servidor que se esté ejecutando; va directamente al archivo recién creado como test.db (si existe, usa el existente)). Luego, ingresé:

create table customers(id int, name varchar(30), address varchar(30));
insert into customers(id, name, address) values (1, ‘Customer 1’, ‘Address 1’);
insert into customers(id, name, address) values (2, ‘Customer 2’, ‘Address 2’);
insert into customers(id, name, address) values (3, ‘Customer 3’, ‘Address 3’);

Mi creatividad para los datos de test es proverbial ;-)

Hay una lista de módulos para Node.js:

https://github.com/ry/node/wiki/modules

Quería usar el módulo de node.js llamado express, lo bajé desde Express. Lo expandí bajo mi directorio de trabajo, con nombre express (podría haberlo puesto en otro lado, y comenzar a probar la variable de entorno NODE_PATH, para que las referencias a ese módulo se resolvieran desde cualquier parte). En el directorio recién creado express, ejecuté:

./install.sh

Luego, descargué el código de node-sqlite, desde grumdig github. (Tengo que probar otra implementación: http://github.com/orlandov/node-sqlite, que tiene llamadas asincrónicas.).

Expandí lo bajado en un subdirectorio de mi directorio de trabajo, pasé a ese directorio, e intenté las instrucciones de instalación:

node-waf configure
node-waf build

Pero la segunda falló. El build del binding de c++ a Sqlite3 requiere el código fuente de Sqlite3 (no vi indicado en ningún lado eso, pero es así). Así que investigué un poco, y ejecuté:

sudo apt-get install libsqlite3-dev

volví a intentar la instalación de node-sqlite, y funcionó.

Nota, si no tienen un compilador c++, como puede pasar en un Ubunto de uso, no de desarrollo, tendran que traerlo con un sudo apt-get g++, por ejemplo. Hay varios compiladores c++ para elegir.

Luego de escribir unos ejemplos de prueba, escribí una aplicación simple (noten que los require se refieren a mi directorio de trabajo):

/**
 * Module dependencies.
 */
var express = require('./express/lib/express');
var sqlite = require("./node-sqlite/sqlite");
var sys = require('sys');
/*
 * Open the database
 */
var db = sqlite.openDatabaseSync("test.db");
/*
 * Creates the web server
 */
var app = express.createServer();
app.get('/', function(req, res){
  res.send('Hello World');
});
app.get('/customers', function(req, res){
  res.writeHead(200, { 'Content-Type': 'text/html' });
  db.query("SELECT id, name, address from customers", function (records) {
    res.write('<h1>Customers</h1>\n');
    res.write('<table>\n');
    for (var i = 0; i < records.length; i++) {
        res.write('<tr>\n');
        res.write('<td>' + records[i].id + '</td>\n');
        res.write('<td>' + records[i].name + '</td>\n');
        res.write('<td>' + records[i].address + '</td>\n');
        res.write('</tr>');
    }
    res.write('</table>\n');
    res.end();
  });
}); 
/*
 * Start web server
 */
app.listen(3000);

Ejecutando la aplicación, la página http://localhost:3000/customers resultó:

Bueno, no será “qué bruto, qué aplicación!” ;-) ;-) pero espero que les sirva por si tienen que pasar por todos estos pasos.

Como siempre, colecciono enlaces. Mis enlaces sobre Node.Js:

http://delicious.com/ajlopez/nodejs

Mi plan: implementar un servidor web en AjSharp, mínima sombra de Node.js; seguir con Node.js con páginas CRUD de una tabla; probar el otro node-sqlite3 con llamadas asincrónicas; generar código para todo esto.

Nos leemos!

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

Posted Fri, Feb 4 2011 10:44 by lopez | 3 comment(s)

Filed under: , , ,

Programando con Windows Azure en Buenos Aires

Gracias a la gente del MUG (Microsoft User Group) de Argentina, el martes 15 y el jueves 17 de Febrero estaré dando un curso gratuito de programación en Windows Azure. Son dos horas el martes y dos horas más el jueves, de 19 a 21hs.

Detalles e inscripción (hay que registrarse) en:

Seminario Gratuito – Programando en Windows Azure

Los temas a dar:

- Cloud Computing en general, IaaS , PaaS, SaaS
- Windows Azure
- Conceptos del HyperVisor, Fabric
- Service Model
- Web Roles
- Worker Roles
- Azure Storage: Blobs, Queues, Tables
- Patrones
- Ejemplos

Voy a mostrar ejemplos, algunos ya publicados acá en posts sobre Azure, y otros nuevos. Azure me parece un tema muy interesante, que permite comenzar a implementar aplicaciones distribuidas, más allá de tener las ventajas del Cloud Computing (escalabilidad, disponibilidad…) Habrán notado que desde hace años aparecen por este blog posts relacionados con aplicaciones distribuidas. Y los que me escucharon en alguna charla (como las de Maratón 2.0) saben que me decanto por explotar el tema de distribución para implementar temas nuevos, como la convergencia de inteligencia artifical y web semántica, y más.

Espero que los que puedan venir encuentren interesante el seminario. Como siempre, quedará publicado por acá el material presentado.

Nos leemos!

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

Posted Thu, Feb 3 2011 10:03 by lopez | 1 comment(s)

Resultado de la ALT.NET Hispano VAN de Programando en Azure

Ya había adelantado esta reunión virtual en:

ALT.NET Hispano VAN Programando en Azure

Gracias a la gente de la comunidad ALT.NET Hispano, pude participar el sábado pasado presentando Programando en Windows Azure. Pueden bajarse la presentación desde mi Skydrive en AzureProgramming201101.pptx. La presentación se basa en slides traducidos de varias presentaciones del Azure Training Kit (el lunes 31 de Enero fue publicada una nueva versión). Les recomiendo bajarse ese material, porque ahí encontraran ejemplos (la gente de Microsoft los llama “hands-on labs”), explicados, y con instrucciones para reproducirlos paso a paso.

La reunión fue grabada, y pueden ver el video en:

Programando con Windows Azure – ALT.NET Hispano Wiki

Estuvimos viendo conceptos generales de Azure, y luego, usando Visual Studio 2010, el Azure SDK:

Windows Azure SDK and Windows Azure Tools for Microsoft Visual Studio (November 2010)

Esa versión SDK 1.3 es para VS2010, y me bajé el archivo VSCloudService.exe (no importa si es 32 o 64 bits) que contiene el SDK y las tools para Visual Studio, no hace falta bajarse el SDK aparte, viene dentro de este archivo. Si tienen que usar Visual Studio 2008, tienen que buscar el SDK 1.2, que fue la última versión que tiene soporte para esa IDE. Lamentablemente, no pueden convivir en la misma máquina el SDK 1.2 y el 1.3.

Y el Azure Storage Explorer, de Neudesic, a bajar desde Codeplex:

Azure Storage Explorer

Durante la charla, me concentré en explicar, para quien es nuevo en Azure, dos grandes temas:

Azure Compute, con Web Roles y Worker Roles.

Azure Storage: con Tables, Queues y Blobs.

Los ejemplos de la charla (con tablas, uso de colas, y blobs) pueden bajarlo del Google code project AjCodeKatas, pueden bajarlos desde:

http://ajcodekatas.googlecode.com/svn/trunk/Azure

Los que no tienen SVN pueden ver la versión “congelada” a estos días:

AjCodeKatasAzure.zip

Ejemplos reducidos de Azure Training Kit que tratan esos temas (si no quieren bajarse TODO el kit), en AzureLabs.zip.

Ya escribí en anteriores posts sobre algunos ejemplos presentados. Tengo que publicar sobre el ejemplo de generación de fractal usando un cliente WinForms local y worker roles que hacen el trabajo (incluido en los ejemplos presentados):

Bibliografía usada:

Programming Windows Azure, Sriram Krisham; O’Reilly
Azure in Action, Chris Hay, Brian H. Prince; Manning
Introducing Windows Azure, Henri Li; Apress

Enlaces que fui usando, los coleccioné en:

http://delicious.com/ajlopez/azure
http://delicious.com/ajlopez/azure+tutorial
http://delicious.com/ajlopez/azure+video
http://delicious.com/ajlopez/azure+storage

Algunos enlaces a destacar:

http://blogs.msdn.com/b/windowsazurestorage/archive/2010/05/10/windows-azure-storage-abstractions-and-their-scalability-targets.aspx
http://blogs.msdn.com/b/windowsazurestorage/archive/2010/12/30/windows-azure-storage-architecture-overview.aspx
http://www.microsoftpdc.com/2009/SVC08
http://code.msdn.microsoft.com/windowsazuresamples
http://blogs.msdn.com/b/olavt/archive/2009/03/12/using-windows-azure-blob-storage-to-store-aerial-images.aspx

Próximos pasos: escribir sobre el ejemplo de Azure Fractal, y escribir un ejemplo de Web Crawler usando Tables, Queues, Web Role y Worker Roles.

Nos leemos!

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

Posted Wed, Feb 2 2011 11:15 by lopez | with no comments