martes, 12 de enero de 2010

Queues y Blobs

Después de leerme documentos y estudiar ejemplos, incorporamos de los blobs y colas (queues), como almacén de datos y comunicación entre el WebRole y el WorkerRole.

En nuestro programa, se crea una cola donde, por cada tipo de modelRoot del archivo xml recivido por el cliente, se obtiene su nombre y se introduce como mensaje. El WorkerRole contiene un bucle esperando a que la cola creada en el WebRole tenga un mensaje, para así empezar la generación del asset específico. Para que saber que no hay que generar más assets, el último mensaje introducido en la cola tiene como texto "end".
Se crea otra cola de en el WorkerRole que tiene la misma función que la anterior pero en la otra dirección, en la cual los mensajes son las clases generadas.

El blob que creamos únicamente se utiliza para almacenar el archivo xml que recibe el WebRole como MemoryStream. El WorkerRole lo utilizará para descargar su contenido.


public void SendQueue(ModelRoot modelRoot)

        {

            //Create Queue for send ModelClass

            QueueStorage qStore = QueueStorage.Create(StorageAccountInfo.GetDefaultQueueStorageAccountFromConfiguration());

            var q = qStore.GetQueue("radarcsend");

            if (!q.DoesQueueExist())

            {

                q.CreateQueue();

            }



            foreach (ModelType type in modelRoot.Types)

            {

                ModelClass modelClass = type as ModelClass;

                if (modelClass != null)

                {

                    //Write modelClass to queue

                    q.PutMessage(new Message(modelClass.Name));



                }

            }

            q.PutMessage(new Message("end"));

        }




public override void Start()

        {

            QueueStorage qStore = QueueStorage.Create(StorageAccountInfo.GetDefaultQueueStorageAccountFromConfiguration());

            

            //Cola lectora

            RoleManager.WriteToLog("Information", "Worker Process entry point called");

            var queue = qStore.GetQueue("radarcsend");

            if (!queue.DoesQueueExist())

                queue.CreateQueue();

            else

                queue.Clear();

            //Cola escritora

            var queueWrite = qStore.GetQueue("radarcreceive");

            if (!queueWrite.DoesQueueExist())

                queueWrite.CreateQueue();

            else

                queueWrite.Clear();



            String stringClass = null;



            while (true)

            {

                Thread.Sleep(10);

                Message message = queue.GetMessage();

                

                if (message != null)

                {

                    string messageString = message.ContentAsString();

                    if (messageString != null)

                    {

                        if (messageString == "end")

                        {

                            queueWrite.PutMessage(new Message("end"));

                            queue.DeleteMessage(message);

                        }

                        else

                        {

                            if (modelRoot == null)

                                modelRoot = ReadModelRoot();



                            if (modelRoot != null)

                            {

                                queue.DeleteMessage(message);

                                stringClass = SearchAndCreate(modelRoot, messageString);

                                queueWrite.PutMessage(new Message(stringClass));

                            }

                        }

                    }

                }

            }

            

  

        }




public List<string> WaitResultQueue()

        {

            List<string> result = null;

            QueueStorage qStore = QueueStorage.Create(StorageAccountInfo.GetDefaultQueueStorageAccountFromConfiguration());

            var queue = qStore.GetQueue("radarcreceive");

            if (!queue.DoesQueueExist())

            {

                queue.CreateQueue();

            }

            Message message = null;

            while (true)

            {

                Thread.Sleep(100);

                message = queue.GetMessage();

                if (message != null)

                {  

                    string messageString = message.ContentAsString();

                    if (messageString != null)

                    {

                        if (messageString == "end")

                        {

                            break;

                        }

                        else

                        {

                            result.Add(messageString);

                            queue.DeleteMessage(message);

                        }

                    }

                }

            }

            queue.DeleteMessage(message);

            queue.DeleteQueue();

            return result;

        }




public void StorageBlob(MemoryStream streamModelRoot)

        {

            streamModelRoot.Position = 0;

            //Create Container for Blob

            StorageAccountInfo account = StorageAccountInfo.GetDefaultBlobStorageAccountFromConfiguration();

            BlobStorage storage = BlobStorage.Create(account);

            BlobContainer container = storage.GetBlobContainer(@"radarc");

            container.CreateContainer(null, ContainerAccessControl.Public);



            //Create Blob for storage ModelRoot

            string uniqueBlobName = "radarcblob";

            BlobProperties prop = new BlobProperties(uniqueBlobName);

            prop.ContentType = "ModelRoot";

            

            BlobContents blob = new BlobContents(streamModelRoot);

            container.CreateBlob(prop, blob, true);

            //RoleManager.WriteToLog("Information", string.Format("Uploaded blob {0} as {1}", fileName, uniqueBlobName));

            prop = container.GetBlobProperties(uniqueBlobName);

            



        }



public ModelRoot ReadModelRoot()

        {

            //Create Container for Blob

            StorageAccountInfo account = StorageAccountInfo.GetDefaultBlobStorageAccountFromConfiguration();

            BlobStorage storage = BlobStorage.Create(account);

            BlobContainer container = storage.GetBlobContainer(@"radarc");

            container.CreateContainer(null, ContainerAccessControl.Public);

            

            string uniqueBlobName = "radarcblob";

            if (container.DoesBlobExist(uniqueBlobName))

            {

                //Read Blob

                BlobProperties prop = new BlobProperties(uniqueBlobName);

                prop.ContentType = "ModelRoot";

                BlobContents blob = new BlobContents(new MemoryStream());



                prop = container.GetBlob(uniqueBlobName, blob, false);



                MemoryStream memoryStream = blob.AsStream as MemoryStream;

                byte[] txt = memoryStream.GetBuffer();

                char[] chars=Encoding.UTF8.GetChars(txt);

                string models=new string(chars);

                if (memoryStream.Length == 0)

                    return null;



                Type[] types = { typeof(Microsoft.VisualStudio.Modeling.Diagrams.CoreDesignSurfaceDomainModel), typeof(BusinessEntitiesDSLDomainModel) };

                Microsoft.VisualStudio.Modeling.Store store = new Microsoft.VisualStudio.Modeling.Store(types);

                ModelRoot modelR = null;

                memoryStream.Position = 0;

                using (Transaction t = store.TransactionManager.BeginTransaction())

                {

                    modelR = BusinessEntitiesDSLSerializationHelper.Instance.LoadModel(store, memoryStream, null, null);

                    t.Commit();

                }

                return modelR;

            }

            return null;

        }


Saludos

El planteamiento

Habiendo adquirido los conocimientos iniciales en aplicaciones CloudService para Windows Azure, nos centramos en el desarrollo del prototipo para adaptar el motor de generación por plantillas de Radarc a la nube, intentando optimizar su tiempo de generación.

Nada más empezar, nos encontramos con el primer problema. El motor de generación por plantillas que usa el subsistema de generación de Radarc sólo se puede ejecutar bajo el entorno de Visual Studio, por lo que en Azure no podríamos utilizarlo, ya que Visual Studio no se encuentra en la nube. La solución la encontramos en Visual Studio 2010, que incorpora una herramienta denominada "Precompiled Text Template" que nos sirvió para sustituir el motor de generación por plantillas y las plantillas de Radarc por clases generadoras de código que representen a dichas plantillas.

Diseñamos, inicialmente, la estructura de nuestro proyecto. La primera parte consta de un cliente que envía el modelo en xml y que luego recibe el resultado (las clases generadas). La segunda parte, el programa que se aloja en Azure, es un WebRole que recibe el modelo del cliente, lo adecúa para tratarlo, y lo envía al WorkerRole para que éste mediante las librerías del motor de generación por plantillas genere los assets correspondientes.

Creamos un proyecto CloudService, con un WebRole y un WorkerRole. Desde el WebRole llamamos a los métodos del WorkerRole como en un programa normal de escritorio. Creíamos que sería sencillo la comunicación entre los roles de la aplicación en Azure, pero ya en la ejecución local del programa vimos que no podía hacerse de esa manera. Por lo tanto, dedicamos el tiempo a buscar algún otro ejemplo un poco más complejo, que tuviera un WebRole y un WorkerRole, dandonos cuenta de que la comunicación entre ellos por lo general se realizaba con Blobs, Queues y Tables.

Visto los ejemplos, nos pusimos manos a la obra para adaptar el programa que ya teníamos a la comunicación mediante el Cloud Storage de Azure.