Archivo de la categoría: Microsoft

Parte II: Cómo usar Custom Load Balance y Affinity en Windows Azure #windowsazure

Introducción

En esta segunda parte del artículo vamos a completar el escenario propuesto anteriormente. La primera parte pueden encontrarla aquí.

El escenario consta de dos máquinas virtuales (ARR1 y ARR2) que actuarán como balanceadores de carga de 3 instancias de la aplicación Web WebAppStateFull. Los Aplication Request Routing (ARR) están expuestos a internet, es decir los clientes hacen sus llamadas HTTTP al puerto 80 del Cloud Services llamado demoarrblog, y estos redirigen las llamadas a los servidores Web que se encuentran en el Cloud Service jpggArrInterno. La gracia es que esas llamadas son a través de la red virtual VirtualNetworkArr que une los dos Cloud Services con direccionamiento privado. Esto quiere decir que los ARR llaman directamente a las direcciones privadas de los Web Servers sin necesidad que estos últimos expongan un EndPoint público, lo que tiene como segunda derivada, mayor seguridad.

0

Este escenario tiene sentido cuando las aplicaciones Web son StateFul y no tenemos opción de cambiarlas a StateLess. Como siempre, debemos notar que staless permite escalar más fácil a las aplicaciones PaaS por lo que es la primera opción al momento de diseñar una aplicación. La aplicación del ejemplo es StateFul, por lo cual cuando es instalada en una granja de servidores como en este caso, es necesario que los balanceadores tenga la característica de afinidad para que no se produzcan errores al usar la aplicación porque el usuario es enviado a otro servidor de la granja en el cual su sesión no existe.

Estas dos cosas vamos a probar en este artículo, balanceo de carga y afinidad en
Windows Azure. Es pre requisito para seguir el paso a paso haber realizado la
configuración de la primera parte del artículo, la cual se encuentra aquí.

Primer paso: crear Proyecto Web staful

Utilizando Visual Studio, creamos un proyecto ASP.NET 4.5 vacio como se muestra en la siguiente pantalla. Esta será nuestra aplicación de Web StateFul la cual llamaremos WebAppStateFul.

1

A este proyecto Web vacío le agregamos una página llamada default.aspx, en la cual agregamos los controlesque aparecen a continuación.

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="default.aspx.cs" Inherits="WebAppStateFul._default" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
    
        Nombre Servidor:
        <asp:Label ID="lbNombreServer" runat="server"></asp:Label>
        <br />
        ¿Es una nueva sessión?
        <asp:Label ID="lbNuevaSession" runat="server" Text="Label"></asp:Label>
        <br />
        LLave de sessión:
        <asp:Label ID="lbSessionLLave" runat="server"></asp:Label>
        <br />
        Contador de request por sessión:
        <asp:Label ID="lbNRequest" runat="server"></asp:Label>
    
        <br />
        <br />
        <strong>Lista de Variables de Session</strong><br />
        <asp:GridView ID="gvListaGalletas" runat="server" CellPadding="4" ForeColor="#333333" GridLines="None" Width="681px">
            <AlternatingRowStyle BackColor="White" />
            <EditRowStyle BackColor="#2461BF" />
            <FooterStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
            <HeaderStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
            <PagerStyle BackColor="#2461BF" ForeColor="White" HorizontalAlign="Center" />
            <RowStyle BackColor="#EFF3FB" />
            <SelectedRowStyle BackColor="#D1DDF1" Font-Bold="True" ForeColor="#333333" />
            <SortedAscendingCellStyle BackColor="#F5F7FB" />
            <SortedAscendingHeaderStyle BackColor="#6D95E1" />
            <SortedDescendingCellStyle BackColor="#E9EBEF" />
            <SortedDescendingHeaderStyle BackColor="#4870BE" />
        </asp:GridView>
    
        <br />
        <strong>Lista de cookies</strong><br />
        <asp:GridView ID="gvListaCookies" runat="server" CellPadding="4" ForeColor="#333333" GridLines="None">
            <AlternatingRowStyle BackColor="White" />
            <EditRowStyle BackColor="#7C6F57" />
            <FooterStyle BackColor="#1C5E55" Font-Bold="True" ForeColor="White" />
            <HeaderStyle BackColor="#1C5E55" Font-Bold="True" ForeColor="White" />
            <PagerStyle BackColor="#666666" ForeColor="White" HorizontalAlign="Center" />
            <RowStyle BackColor="#E3EAEB" />
            <SelectedRowStyle BackColor="#C5BBAF" Font-Bold="True" ForeColor="#333333" />
            <SortedAscendingCellStyle BackColor="#F8FAFA" />
            <SortedAscendingHeaderStyle BackColor="#246B61" />
            <SortedDescendingCellStyle BackColor="#D4DFE1" />
            <SortedDescendingHeaderStyle BackColor="#15524A" />
        </asp:GridView>
        <br />
        <strong>HTTP Header</strong><br />
        <asp:GridView ID="gvHttpHeader" runat="server" BackColor="#CCCCCC" BorderColor="#999999" BorderStyle="Solid" BorderWidth="3px" CellPadding="4" CellSpacing="2" ForeColor="Black">
            <FooterStyle BackColor="#CCCCCC" />
            <HeaderStyle BackColor="Black" Font-Bold="True" ForeColor="White" />
            <PagerStyle BackColor="#CCCCCC" ForeColor="Black" HorizontalAlign="Left" />
            <RowStyle BackColor="White" />
            <SelectedRowStyle BackColor="#000099" Font-Bold="True" ForeColor="White" />
            <SortedAscendingCellStyle BackColor="#F1F1F1" />
            <SortedAscendingHeaderStyle BackColor="#808080" />
            <SortedDescendingCellStyle BackColor="#CAC9C9" />
            <SortedDescendingHeaderStyle BackColor="#383838" />
        </asp:GridView>
    
    </div>
    </form>
</body>
</html>

Luego agregamos el código C# que tiene la lógica de la aplicación.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Collections;

namespace WebAppStateFul
{
    public partial class _default : System.Web.UI.Page
    {
        

        protected void Page_Load(object sender, EventArgs e)
        {
            lbNombreServer.Text = System.Environment.MachineName;
            if (Session["llave"] == null)
            {
                //Nueva session
                lbNuevaSession.Text = "Si";
                Session["llave"]= string.Format("{0}_{1}_{2}", System.Environment.MachineName, DateTime.Now.ToShortTimeString(), Request.Browser.Browser.ToString());
                Session["nRequest"] = 0;
            }
            else 
            {
                lbNuevaSession.Text = "NO";
                
                Session["nRequest"] = (int)Session["nRequest"] + 1;
            }
            lbSessionLLave.Text = (string)Session["llave"];
            lbNRequest.Text = ((int)Session["nRequest"]).ToString();
            
            ArrayList listaVarSession = new ArrayList();
            for (int i = 0; i < Session.Count; i++)
            {
                listaVarSession.Add(Session[i]);
            }
            gvListaGalletas.DataSource = listaVarSession;
            gvListaGalletas.DataBind();

            //gvListaCookies
            ArrayList colCookies = new ArrayList();
            for (int i = 0; i < Request.Cookies.Count; i++)
            {
                colCookies.Add(Request.Cookies[i]);
            }
            gvListaCookies.DataSource = colCookies;
            gvListaCookies.DataBind();
            //http header
            ArrayList httpLista = new ArrayList();
            for (int i = 0; i < Request.Headers.Count; i++)
            {
                httpLista.Add(Request.Headers.GetKey(i) +"="+ Request.Headers[i]);
            }
            gvHttpHeader.DataSource = httpLista;
            gvHttpHeader.DataBind();
        }
    }
}

Esta aplicación captura el nombre físico de la instancia de la aplicación Web donde se está ejecutando el requerimiento dentro de la Server Farm. Lugo, si es el primer llamado de ese cliente a ese servidor crear una variable de sesión llamada llave para identificar si el servidor reconoce o no al cliente. Por último, en otra variable de sesión lleva la cuenta de los requerimientos que cada cliente lleva en ese servidor. Esto es una aplicación StateFul porque si el cliente es enviado a otro servidor se pierde la cuenta.

La siguiente imagen muestra lo que la página produce después de cargarla un par de veces.

1y1

Segundo paso: Proyecto Cloud

Ya tenemos una aplicación Web lista, la que usaremos como ejemplo nuestro Web Site Stateful. Ahora, para poder publicar la aplicación en Azure. Para esto, tenemos que agregar un nuevo proyecto del tipo Cloud vacío, como se muestra en el siguiente diálogo.

3

Para incluir el proyecto Web existente a la solución Cloud, debemos agregar un Web Role de una solución existente como se muestra a continuación.

4

En este punto ya tenemos un proyecto Cloud y podemos probarlo utilizando el emulador local. Para esto, configuramos el proyecto CloudServiceWeb como Set As Startup Prjoject y luego utilizando F5 ejecutamos la aplicación.

Podemos ver en Windows Azure Compute Emulator que la aplicación se ejecuta en una instancia y en el Browser los datos del servidor donde se ejecuta y variables de sesión.

5

Para el escenario que vamos a montar en Azure, vamos a utilizar 3 instancias, lo que nos va a ayudar  a ver claramente cómo el servicio ARR realiza el balanceo de Carga. Para configura la cantidad de instancias, vamos a la configuración del rol y cambiamos la cantidad de instancias de 1 a 3.

6

El Segundo cambio que debemos hacer es configurar el EndPoint externo en el puerto 80, así podrán los clientes acceder al servicio una vez publicado en la Nube.

6y1

La ultima configuración que hacemos es la red virtual (virtualNetwordArr) y sub red (servidoresWe) donde las instancias de servicio Web se van a desplegar. Esto se configura manualmente en el archivo XML ServiceConfiguration.Cloud.cscfg

<?xml version="1.0" encoding="utf-8"?>
<ServiceConfiguration serviceName="cloudServiceWeb" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration" osFamily="3" osVersion="*" schemaVersion="2013-03.2.0">
  <Role name="WebAppStateFul">
    <Instances count="3" />
    <ConfigurationSettings>
      <Setting name="Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString" value="UseDevelopmentStorage=true" />
    </ConfigurationSettings>
  </Role>
  <NetworkConfiguration>
    <VirtualNetworkSite name="virtualNetwordArr" />
    <AddressAssignments>
      <InstanceAddress roleName="WebAppStateFul">
        <Subnets>
          <Subnet name="servidoresWe" />
        </Subnets>
      </InstanceAddress>
    </AddressAssignments>
  </NetworkConfiguration>
</ServiceConfiguration>

Por último, podemos hacer el despliegue en la nube siguiendo el asistente de publicación del proyecto Cloud.

7

Una vez terminado el despliegue de la solución podemos revisar en el portal de administración de Azure los recursos que la red virtual virtualnetwordarr tiene desplegados. En la lista de recursos vemos las 3 instancias de nuestra aplicación junto con la máquina virtual que tiene el servicio de ARR.

8

Tercer paso: Pruebas del proyecto Cloud

Para probar el servicio, utilizamos un par de navegadores diferentes y vamos a la url http://demoarrblog.cloudapp.net/ del Cloud Service donde hicimos el deploy de la aplicación Web. Como configuramos un EndPoint externo en el puerto 80, accedemos directamente como muestra la siguiente imagen. Podemos ver que cada cliente es enviado a diferentes instancias de nuestra aplicación. Este balanceo es el que hace Windows Azure de manera automática, del cual no tenemos control por ahora.

9

Cuarto paso: Configuración ARR

Una vez probada la aplicación Web y entendido el comportamiento del balanceador de carga por defecto, vamos a configurar el servicio Aplication Request Routing ARR en la máquina virtual para tomar control de la forma como balanceamos la carga de usuarios y la afinidad de los mismos con la primera instancia con la que se conectaron. El escenario se muestra en la siguiente figura.

La idea es que el cliente llama al servicio a la URL del  Cloud Service del ARR en la URL http://demoarrblog.cloudapp.net/ y ahí el ARR toma control del requerimiento y lo redirige hacia una de las instancias del servicio Web.

9y1

Las instancias del servicio web WebAppStateFul sólo tienen que recibir requerimientos desde el servicio de ARR por lo cual no es necesario que exponga un EndPoint público. El ARR llamará directamente al servicio utilizando el direccionamiento privado 10.0.0.x de la sub red servidoreswe En este caso, vamos a publicar un EndPoint interno porque los clientes finales no van a acceder directamente a este servicio sino que lo harán a través de los servidores ARR. Para que sea más claro, vamos a utilizar el puerto 90 y no el 80 para publicar el servicio.

Volvemos a hacer deploy del servicio con la nueva configuración.

10

Una vez actualizada la configuración del Cloud Servicies, tenemos que configurar el ARR para que diriga los requerimientos que recibe hace los servicios Web. Nos conectamos a la maquina vitual utilizando RDP y podemos comprobar que tenemos conectividad con las instancias de la aplicación web utilizando direccionamiento privado. Para validar podemos hacer ping al 10.0.0.12 y usando el navegador podemos cargar http://10.0.0.12:90

11

La configuración del servicio ARR se hace en Internet information Server Manager. Al igual como lo hicimos en la primera parte del artículo, vamos a configurar una granja de servidores. En la misma granja antes creada, borramos los servidores que contiene y agregamos las tres instancias de la aplicación Web:

  • 10.0.0.12 puerto 90
  • 10.0.0.13 puerto 90
  • 10.0.0.14 puerto 90

12

Para la primera prueba no utilizaremos Server Affinity para así observar cómo funciona al balanceador. Para esto, desmarcamos la opción Client Affinity de la granja de servidores. Esto significa que vamos dejar que los clientes fluyan entre todos los servidores de la granja.

13

ARR permite diferentes criterios de balanceo, nosotros utilizaremos Weighted Round Robin con una distribución de carga custom, que dejaremos en 33.33% para que todos los servidores reciban la misma cantidad de requerimientos de clientes.

14

Quinto paso: Pruebas de balanceo sin afinidad

Una vez realizada la configuración del ARR podemos probar utilizando un solo cliente. Utilizando el browser cargarnos la URL del Cloud Services de la máquina virtual con ARR y obtenemos el siguiente resultado en la primera llamada. Nos responde el servidor RD00155D53D6C8 y reconoce que la sesi[on es nueva, es decir es el primer llamado.

15

Ahora, realizamos llamados sucesivos y obtenemos las respuestas que se muestran en la siguiente tabla. El comportamiento es exactamente el esperado, el cliente es dirigido a cada uno de los 3 servidores que forman la Web Farm de manera secuencial lo que crea 3 sesiones diferentes para el cliente, una en cada instancia de la aplicación. Esto es el problema para las aplicaciones StateFul ya que el cliente en rigor es solo uno por lo que debería tener solo una sesión independiente de la cantidad de instancias de la granja.

# Request Nombre Servidor ¿Es una nueva sesión? Llave de sesión Contador de request por sesión

1

RD00155D53D6C8

RD00155D53D6C8_9:02 PM_Firefox

0

2

RD00155D53D115

RD00155D53D115_9:03 PM_Firefox

0

3

RD00155D53C1CB

RD00155D53C1CB_9:03 PM_Firefox

0

4

RD00155D53D6C8

No

RD00155D53D6C8_9:02 PM_Firefox

1

5

RD00155D53D115

No

RD00155D53D115_9:03 PM_Firefox

1

6

RD00155D53C1CB

No

RD00155D53C1CB_9:03 PM_Firefox

1

7

RD00155D53D6C8

No

RD00155D53D6C8_9:02 PM_Firefox

2

Para solucionar el problema de las múltiples sesiones debido a múltiples instancias en una granja de servidores tenemos dos opciones. Primero, y la más recomendada,  modificar la aplicación para que sea StateLess. Si esto no se puede hacer, tenemos la segunda opción que es crear afinidad entre el cliente y la instancia del servidor que lo atiende primero.

ARR nos permite crear afinidad de una manera muy simple. Configuramos en Server Affinitty la opción de Client Affinity y con esto ARR agrega una cookie en el cliente que mamamos en este caso  ARRAffinity. Esta cookie almacena la instancia que ese cliente está utilizando, lo que le permite al ARR desde el segundo Request en adelante seguir retueando al cliente con la primera instancia que sirvió a ese cliente. De esa forma la sesión del cliente se mantiene con ese servidor y no tenemos problema con la aplicación StateFul.

La configuración la realizamos a nivel de Server Farm, en la opción de Server Afinnity como muestra la siguiente figura.

16

Sexto paso: Pruebas de balanceo con afinidad

Una vez configurado ARR con afinidad podemos probar el comportamiento de la aplicación. Para esto abrimos dos nuevas instancias del navegador y hacemos el primer request. Luego comenzamos a recargar la aplicación y las respuestas se muestran en las siguientes capturas de pantalla.

Primer request de ambos navegadores.17

Segundo request de ambos navegadores.18

Tercer request de ambos navegadores.

19

Las respuestas que obsérvanos nos muestran que el primer cliente (Internet Explorer) fue ruteado por el ARR la instancia RD00155D53D6C8 en el primer llamado. El segundo cliente (FireFox) a su vez fue enviado a la instancia RD00155D53D6C8. Todas las siguientes peticiones de cada cliente, se mantienen en la misma instancia! Esto es afinidad funcionando.

Pueden ver también que la tabla donde aparecen las cookies, aparece la cookie llamada ARRAfinnity, llave con al cual ARR logra la afinidad con el servidor.

Séptimo paso: Agregar Segundo ARR

Todo sistema que busca tener alta disponibilidad requiere como base no tener puntos únicos de falla, en el ejemplo hasta ahora desarrollado la aplicación Web tiene 3 instancias por lo cual no debemos preocuparnos por la falla en una de ellas. Si hay una falla en una instancia, los clientes que tienen afinidad establecida con esa instancia van a experimentar un error de conexión (la instancia esta caída) y el ARR los enviará a conectarse a otra instancia donde crearan una nueva sesión.

El punto único de falla, hasta ahora, en el ejemplo es el ARR ya que sólo tenemos una instancia. Queremos llegar a la siguiente configuración.0

Para completar este escenario, debemos crear un segundo Windows Server 2012 y configurar el servicio de ARR en esa nueva máquina virtual. Para hacer esto, seguimos los mismos pasos explicados en detalle en la primera parte del artículo, específicamente en los pasos Crear un servidor Windows Server 2012 , Configuración de IIS en Windows Server e Instalación de Instalación de Aplication Request Routing (ARR). Esto se encuentra en este link.

Las consideraciones que debemos tener para crear esta segunda máquina virtual son el nombre (ARR2) y ubicarla en el mismo Cloud Service que la primera, como se muestra en la siguiente captura de pantalla.

20

Una vez Instalado ARR, se configura la granja de servidores con las 3 instancias de la aplicación Web de la misma manera como se hizo con el primer ARR. Esto incluye configurar Server Affinity además de los 3 servidores de la granja y el criterio de balanceo de carga.

22

Por último, debemos configurar el Endpoint público de la máquina virtual en el
puerto 80. Como en el Cloud Service el servicio Web se va a balancear entre las
dos máquinas virtuales, se crea un EndPoint del tipo Load-Balance como se
muestra a continuación.

23

El puerto a balancear es el 80 (externo) y el puerto de la máquina virtual (interno) también es el 80.

24

Una vez creado el EndPoint podemos ver en la configuración de la máquina virtual que el puerto ha quedado configurado y balanceado. Con esto eliminamos el punto único de falla de la arquitectura de ejemplo.

25

Octavo paso: Probar la configuración

Nuevamente utilizamos 3 clientes para realizar las pruebas. Los 3 clientes se conectan a dos servidores, lo cual podría pensarse que es un error pero no lo es ya que cada ARR tiene su propio registro de balanceo, por lo cual, ARR1 puede haber enviado el primer request que recibió a RD00155D53C1D5 y ARR2 al recibir su primer request puede hace lo mismo. Por eso ese servidor aparece dos veces. Ahora, si se fijan el tercer cliente va a otro servidor. Si recargamos las páginas de los 3 clientes observamos que mantienen afinidad con el servidor que los atendió primero, por lo que hemos comprobado que tenemos listo la configuración de nuestro ejemplo.

28

Conclusiones

En estos dos artículos hemos desarrollado un escenario donde podemos tomar control del método con que se balancea la carga entre las múltiples instancias de una aplicación Web.

Además, para las aplicaciones StateFul que no podemos modificar, aprendimos a configurar la característica de Server Afinnity de ARR que permite asegurar que el cliente se mantendrá conectado a la instancia de la aplicación que lo atendió en el primer llamado que es en la cual creo su sesión.

Como corolario al ejercicio, tuvimos la necesidad de conectar un Cloud Service IaaS, donde están contenidas las máquinas virtuales de ARR, con un Cloud Services de PaaS. En este último se desplegaron las instancias de la aplicación Web. Esto lo logramos utilizando redes virtuales, que son la vía de comunicación interna/ directa entre  Cloud Services independiente si son PaaS o IaaS. Esto es una de las grandes ventajas de la plataforma Azure que permite construir sistemas hibridos combinando la potencia de PaaS con la flexibilidad de IaaS en nuestros sistemas.

Links relacionados

Cómo usar Custom Load Balance y Affinity en Windows Azure #windowsazure

Introducción

Windows Azure tiene la característica de balanceador de carga incluido dentro del servicio, no es un cobro extra como en otras soluciones de Nube. Este servicio de balanceo está disponible tanto para aplicaciones Cloud Services (PaaS) como para servicios que corren en máquinas virtuales (Iaas).

El balanceador de Azure tiene el criterio de distribución de carga llamado Round Robin y no soporta sticky session, hasta ahora. Esto produce un reto para las compañías que tienen aplicaciones que no son State Less.

En ese contexto, y no como la primera opción a recomendar, podemos hacer un Walkaround utilizando nuestro propio sistema de balanceo basado en Aplication Request Routing (ARR) de Internet Information Server (IIS). Con esto podemos implementar un escenario donde tenemos ARR corriendo en máquinas virtuales, que balancean la carga hacia servidores Web que se encuentren en otro Cloud Service con la opción de afinidad.

Este es un escenario muy potente porque estamos tomando el control del balanceador, agregando la capacidad de afinidad de sesiones y conectado IaaS con PaaS dentro del mismo sistema. . Aquí, una pieza clave para unir los dos mundos (IaaS con PaaS) son las redes virtuales.El escenario que vamos a implementar se muestra en la figura 1.

escenario de demo

Este articulo lo divido en dos partes, para que sea más simple su lectura. La primera parte configura el primer ARR y hace pruebas de balanceo junto con afinidad.

En la segunda parte vamos a completar el escenario de la figura 1.

Paso a paso para implementar ARR con balanceador y afinidad

Primero vamos a implementar un escenario más sencillo que nos permita entender cómo se implementa y configura ARR en Windows Azure. La idea es solo crear un ARR y que este balancee tráfico a dos sitios web de terceros para luego agregarle afinidad. El escenario de prueba inicial lo podemos ver en la figura 2.

Escenario de prueba

1.      Crear una Red Virtual

En la figura 1 se muestra el escenario que buscamos armar en este artículo. Para lograrlo debemos comunicar directamente el Cloud Service que contiene las máquinas virtuales con el que contiene las aplicaciones Web. Esto es conectar IaaS con PaaS, una de las maravillas que nos permite hacer Azure.

La unión des diferentes Cloud Services se logra a través del uso de una red virtual. La red virtual debe ser creada primero, para así estar disponible al momento de crear las máquinas virtuales y aplicaciones Web.

Para crear una red virtual, en el portal se selecciona la opción New, Networks y Custom Create como muestra la figura 3.

i1

Luego, ingresamos el nombre de la red y el grupo de afinidad. Es un requisito tener un grupo de afinidad para tener una red virtual.

i2

El siguiente paso en el asistente es la creación y asignación del espacio de direccionamiento. Para asignar direcciones tenemos dos opciones de nomenclatura, COUNT y CIDR. CDIR es la clásica de los administradores de red, que definen máscaras y con eso determinan la cantidad de direcciones IP del segmento. Por otra parte, COUNT es el método para desarrolladores donde defines el número de direcciones IP que necesitas en el segmento de manera directa y más simpe.

Para nuestro escenario vamos a definir una sub red para los balanceadores (máquinas virtuales con ARR) y otro para las aplicaciones Web (PaaS)

i3

Con esto ya tenemos definida nuestra virtual la cual será utilizada en los próximos pasos.

2.      Crear un Servidor Windows Server 2012

Vamos a crear un servidor con Windows Server 2012 en una Máquina virtual de Windows Azure. Para ello, desde el portal se selecciona la opción de crear una máquina virtual desde la galería. Seleccionamos la imagen de Windows Server 2012 Datacenter y avanzamos en el asistente de creación.

i4

Le asignamos el nombre de ARR1 a la VM y definimos el usuario administrador. La versión del realeas no es importante para nuestros objetivos porque básicamente solo necesitamos IIS.

i5

En el paso 3 debemos definir el DNS name, en este caso demoARRblog. Este es el DNS del Cloud Service y será el mismo que tendrá la segunda máquina virtual con ARR. Muy importante en este paso es seleccionar la red virtual que creamos en el primer paso y la sub net de balanceadores, como se muestra en la siguiente figura.

i6.0

Por ultimo nos preguntan por el Availability Set, para este demo no lo necesitamos, en producción obvio que sí, lo dejamos en blanco.

Con esto ya hemos creado nuestro primer servidor Virtual en la subnet de balanceadores. Una vez creada la VM, podemos ver en el Dashboard de la red virtual que aparece el recurso ya alocado en la red y sub red definidas.

i6.0.1

3.      Configuración de IIS en Windows Server

Lo primero que vamos ha hacer es modificar las configuraciones de seguridad del explorador para poder acceder a internet. Para los administradores seleccionamos OFF.

i7

Luego en Server Manager, agregamos el ROL de Web Server (IIS) y las herramientas de administración.

i8

A continuación en los servicios del rol agregamos los por defecto para luego confirmar la instalación.

i9

4.      Instalación de Aplication Request Routing (ARR)

Ahora, utilizando Internet Information Services (IIS) manager, vamos a instalar ARR. Para ello, al entrar al IIS Manager, nos aparece el siguiente dialogo donde nos preguntan si queremos conectarnos a la plataforma de componentes Web, le decimos que sí.

i12

Esto nos lleva al sitio Web donde podemos bajar e instalar gratuitamente Web Plaform Installer. Lo bajamos e instalamos en nuestro servidor virtual.

i13

Luego buscamos el Web Platform Installer 4.5 por ARR como se muestra en la siguiente figura. Lo agregamos aprontando el botón ADD y luego lo instalamos.

i15

El proceso de instalación termina con la siguiente pantalla de confirmación.

i18

5.      Publicar Puerto 80/http de la Máquina Virtual

Para poder acceder al servidor Web de nuestro servidor Virtual debemos exponer el servicio a través de un EndPoint. Existen los EndPoint externos e internos. Para que un usuario pueda acceder al nuestro servidor Web, tenemos que crear un EndPoint externo en el puerto 80.

Para hacer esto, vamos al portal de Azure y en la máquina virtual buscamos la pestaña que dice ENDPOINTS. Ahí apretamos agregar y se inicia el asistente para crear el EndPoint. Vamos a crear uno con las siguientes características

  1. Nombre: WWW
  2. Protocolo: TCP
  3. Puerto público: 80
  4. Puerto Privado: 80

i8.1

Ahora podemos conectarnos utilizando un Browser a nuestro servidor virtual.

i18.1

6.      Crear una Server Farm de pruebas con Aplication Request Routing (ARR)

En este punto ya tenemos el primer servidor virtual con ARR instalado y podemos crear ya la primera server FARM para poder probar las características de ARR. Una Server FARM es una granja de servidores que se comportan como un solo servidor lógico que está compuesto por varias instancias de servidores, físicos o virtuales.

El escenario de pruebas que usaremos se ilustra en la siguiente imagen. La idea es que el ARR1 sirva como balanceador de carga que dirige los requerimientos que se envían a http://demoarrblog.cloudapp.net a dos sitios web públicos, www.dell.com y www.amazon.com.

Escenario de prueba

Primero creamos un Server Farm. Para esto vamos a IIS Manager y vamos a ver que ahora aparece dentro del árbol de nuestro servidor una hoja que se llama Server Farm. Esto es lo que nos agregó la instalación de ARR.

i19

Si iniciamos el asistente de crear una nueva Server Farm, lo primero que nos pregunta es el nombre y si está estará Online. En nuestro caso, vamos a llamarla BalanceadorSitiosExternos y estará Online. Como siguiente paso vamos a agregar los servidores que componen la granja, 143.166.224.244 para DELL y 176.32.98.166 para Amazon.

i22.1

Cuando damos Finalizar al asistente, aparece el siguiente dialogo que nos pregunta si queremos que las reglas de ruteo se creen automáticamente. Le decimos que sí, ya que esas son las reglas que buscamos crear para redirigir los requerimientos hacia los dos sitios externos.

i23

Por último vamos ha configurar el método de distribución de carga. Para ello vamos a nuestra granja y seleccionamos Load Balance.

i26

Vamos a utilizar el criterio de distribución de carga llamada Weigted Round Robin, que distribuye los requerimientos de manera homogénea entre ambos sitios si los configuramos con el mismo peso relativo, como aparece en la siguiente imagen.

i27

Una vez que aplicamos los cambios, podemos cargar varias veces el sitio Web http://demoarrblog.cloudapp.net y vamos a ver el comportamiento del balanceador funcionado. Alternadamente nos muestra el contenido del sitio Web de DELL y Amanzon, en la URL del Cloud Service!

Podemos ver en las dos siguientes capturas de pantalla la prueba de ejecución.

i27.1i27.2

7.      Pruebas de Server Affinity con Aplication Request Routing (ARR)

La última prueba que vamos a realizar es la de afinidad (affinity). La afinidad es la capacidad de mantener al cliente final conectado siempre al mismo servidor de la granja de servidores. Por ejemplo, si un usuario entra en nuestro servicio y el balanceador lo envío la primera vez al sitio Web de Dell, la afinidad hace que todos los siguientes requerimientos de ese cliente sean dirigidos hacia ese servidor hasta que se cumpla un tiempo de inactividad o ese servidor se encuentre fuera de línea.

La opción de affinity se utiliza para que aplicaciones Web que no están preparadas para ser balanceadas porque no son state less. Cuando se trabaja en la nube, especialmente en PaaS, lo ideal es que nuestras aplicaciones Web sean State Less porque eso permite escalar sin ningún problema. El uso de ARR es un Walk Arround para las aplicaciones Web que no cumplen con esta característica de no tener estado.

Para configurar Server Affinity con Aplication Request Routing (ARR) vamos a la opción de Server Affinity de nuestra granja de servidores como muestra la figura.

i29

En esa opción seleccionamos Client Affinity y aplicamos el cambio. Esta característica se basa en el uso de cookies en el cliente, que permite al ARR conocer cuál fue el servidor de la granja que le asigno en el primer requerimiento de ese cliente y continúa enviándolo ahí.

i30

Una vez configurado podemos probar que el cliente siempre será enviado al mismo sevidor. En mi caso, con Explorer  cargue mi sitio y me envió al servidor de Dell. Todos los siguientes requerimientos con ese Browser van al mismo sitio de Dell.

i31

ara comprobar la afinidad, ahora utilizaré otro browser para cargar el sitio. Esto equivale a otro cliente, por lo que me envía a Amazon. Todos los recargar que hago me envían al mismo servidor.

i32

Próximos pasos

En esta primera parte hemos creado y configurado un servidor virtual con Aplication Request Routing (ARR). En el siguiente artículo voy a desarrollar el escenario completo, como se muestra en la figura 1, es decir una aplicación Web corriendo en PaaS, balanceada con ARR con mis propios criterios y con Server Affinity para crear afinidad entre el cliente y el servidor que lo atiende en la Server Farm.

Links relacionados

 

Te invitamos a Windows Azure Tech Series #windowsazure

En Microsoft Chile se está realizando una serie de conferencias técnicas llamadas Windows Azure Tech Series.
Están muy interesantes, los temas y fechas son los siguientes.

Fecha Hora Tema Speaker
30 de Mayo 18:30 a 20:30 SQL en Windows Azure: Virtual Machines, SQL como Servicio y Entornos   Híbridos Pablo César García
4 de Junio 18:30 a 20:30 Big Data en Windows Azure: Como   implementar hadoop, hive, pig-latin, mahout, pegasus y flume en windows   azure. Pablo César García
11 de Junio 18:30 a 20:30 Active Directory en la Nube: Opciones de identidad y   federación con Windows Azure Alejandro Pacheco
20 de Junio 18:30 a 20:30 Manejo de multimedia optimizado en la nube con Windows Azure   Media Services Juan Pablo García
25 de Junio 18:30 a 20:30 Plataformas de desarrollo Open Source en Windows Azure Hans Nemarich

Para más detalles revisa el calendario aquí.

cloud

Presentación Keynote: Transformado la nube en una oportunidad de crecimiento #windowsazure

En el evento Descubre las ventajas de llevar tu producto a la nube en Lima, presentamos este Keynote

Links relacionados

Evento: Descubre las ventajas de llevar tu producto a la nube en Lima Peru

El próximo lunes voy a participar del evento Descubre las ventajas de llevar tu producto a la nube.  Esta es la invitación para los participantes, vamos a revisar la Nube desde principio a fin tanto para desarrolladores como profesionales de IT.

Principales temas :
• Transformado la nube en una oportunidad de crecimiento
• IaaS: Extendiendo tu datacenter a la nube
• PaaS: Servicios y sitios web en la nube
• Aplicaciones móviles con back-end en la nube
• Administrando la nube pública y privada
• Planificando tu proyecto cloud

Expositores:
• Juan Pablo García – Microsoft Corp
• Jorge Oblitas – Microsoft Perú
• José Luis Yapur – Microsoft Perú
• Luis Quiroz – Tecnofor

eventoPeru

Fecha del Evento:  Lunes, Mayo 27, 2013 – 09:00 to 13:00
Ubicación del Evento: Novotel (Av. Víctor Andrés Belaúnde 198 – San Isidro)
Ciudad del Evento: Lima
Provincia del Evento:Lima

Windows Azure SDK 2.0 Video

La semana pasada se lanzó en SDK 2.0 de Windows Azure. Aquí hay un video de Channel9 donde Scott Guthrie explica las nuevas características del mismo.

sdk2.0video

Se puede bajar el SDK 2.0 de Windows Azure desde aquí.

Links relacionados

Diferencias entre Windows Azure Web sites y Web Role (cloud Service)

Un partner que desarrolla una red social me pregunto la diferencia entre utilizar Web Sites o un Web Role (Cloud Services).

Hay un muy buen post de MSDN que explica las diferencias y los escenarios de cuando utilizar cada uno.

Windows Azure Websites, Cloud Services, and VMs: When to use which?

La siguiente tabla resumen las características principales de cada opción, lo cual puede servirnos para decidir cual es opción que mas nos sirve.

Feature Web Sites Web Roles
Access to services like Caching, Service Bus, Storage, SQL Azure Database Yes Yes
Support for ASP.NET, classic ASP, Node.js, PHP Yes Yes
Shared content and configuration Yes No
Deploy code with GIT, FTP Yes No
Near-instant deployment Yes No
Integrated MySQL-as-a-service support Yes Yes
Multiple deployment environments (production and staging) No Yes
Network isolation No Yes
Remote desktop access to servers No Yes
Ability to run programs with elevated permissions No Yes
Ability to define/execute start-up tasks No Yes
Ability to use unsupported frameworks or libraries No Yes
Support for Windows Azure Connect/ Windows Azure Network No Yes

Links relacionados

Windows Azure Active Directory en la pizarra #windowsazure

En Channel 9 hay una muy Buena explicación de Windows Azure Active Directory. Por el alcance de nombre, he recibido muchas peguntas sobre el tema. La más preocupante fue «puedo dejar de usar Active directory en mi empresa y reemplazarlo por Windows Azure Active directory?»

Este video es muy fácil de seguir y entender las diferencias entre los dos servicios.

waad

Links relacionados

  1. Windows Azure: ASP.NET en Cloud Service utilizando MongoDB
  2. Windows Azure Media Services: Publicar videos para IOS y Windows Phone

Windows Azure: ASP.NET en Cloud Service utilizando MongoDB

Introducción

El mundo de las bases de datos está cambiando vertiginosamente, debido a las nuevas necesidades de información y cantidad de datos que se almacenan actualmente en los sistemas de información. Dentro de este contexto ha aparecido Mongo DB que es una base de datos  no relacional, o cómo le gusta al marketing referirse una base de datos NoSql.

MongoDb es una base de datos orientada a documentos en formato Json que permite esquemas dinámicos. Dentro de las principales características de la base de datos podemos encontrar:

  1.  Indexación por cualquier atributo, es decir búsqueda rápida por cualquier atributo del documento (registro en el mundo relacional)
  2. Soporta Mirror para HA
  3. Escalamiento horizontal utilizando Sharding
  4. Soporte para Map/Reduce, para procesos de procesamiento y agregación flexible de datos

Pueden encontrar más información de MongoDB aquí.

En este Post vamos a hacer una simple aplicación Web (Azure Cloud Service) con C# que utiliza MongoDB para almacenar Notas ingresadas en la Página Web. Además, permite agrupar las notas en libros para mostrar las capacidades de esquema flexible y agrupación de documentos dentro de otros documentos.

Requerimientos del ejemplo

Para poder desarrollar este ejemplo se necesita:

  1. Una Base de datos de MongoDB
  2. Instalar el Driver de C# para Mongo
  3. Windows Azure SDK

Paso a paso de la implementación

Paso 1: Crear una instancia de MongoDB

Para desarrollar el ejercicio vamos a crear una instancia de MongoDB en Azure utilizando el Add-On que ofrece MongoLAB en windows Azure. Para ello seguimos el asistente que se muestra en la siguiente figura, el cual se inicia utilizando el botón NEW, Store y se busca en la lista la opción de MongoDB.

img1

Una vez creada la base de datos de MongoDB, la cual es provista en el modelo DBaaS, podemos ver el panel de control que se muestra en la figura 2.

img2

Para administrar nuestra base de datos Mongo, tenemos que hacer click en el botón Manage de la botonera al pie de página, donde llegaremos a la consola de administración Web de MongoLab, la cual se muestra en la figura 3.

img3

Con esto ya estamos listos para codificar nuestra aplicación.

Paso 2: Crear el proyecto Cloud Service

Para crear nuestro proyecto Cloud Services utilizaremos Visual Studio, creamos un nuevo proyecto del tipo Cloud Services, sin ningún role dentro, como se muestra en la figura 4.

img4

Ahora, vamos a agregar a la solución un proyecto ASP.NET Empty Web Application utilizando el botón derecho sobre la solución, opción Add new proyect. Usamos un Web vacio para que sea más simple poner foco solo en los aspectos de Mongo en el ejemplo.

Cuando tenemos los dos proyectos, debemos agregar el ROL Web al cloud Services. Para ello, utilizando el botón derecho sobre ROLES, utilizamos la opción Add … Web Project in the Solution y seleccionamos nuestro proyecto Web vacío.

Por úlitmo debemos instalar el driver de C# para Mongo. Esto lo hacemos utilizando el módulo Package Manager Console con el comando Install-Package mongocsharpdriver. La figura 5 muestra la consola después de instalarlo.

img5

En este punto tenemos la solución creada y lista para comenzar a programar.

Paso 3: Crear clase de acceso a datos

Para acceder a Mongo vamos a utilizar una clase de acceso a datos, que nos simplifica y aísla el origen de datos de la aplicación. Para esto, creamos una clase llamada DAC. En esta clase agregamos las referencias que necesitamos para Mongo.

using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Bson.Serialization.IdGenerators;
using MongoDB.Driver;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.UI.WebControls;

Con Mongo tenemos dos opciones para poder trabajar con los documentos. La primera es al estilo fuertemente tipados, como estamos acostumbrados por ejemplo a trabajar en C# y la otra es utilizar BsonDocumet es decir sin tipos conocidos en tiempo de programación. Aquí, vamos a trabajar primero con objetos con tipos y luego lo haremos son tipos. Obviamente la segunda aproximación da más flexibilidad y potencia para usar las colecciones de documentos de Mongo.

Paso 4: Crear una clase tipo Nota y DAC

Utilizando la primera aproximación de trabajo vamos a crear la siguiente clase

public class DocumentoNota
    {
        public DocumentoNota()
        {
            Date = DateTime.UtcNow;
        }
        private DateTime date;
        [BsonId(IdGenerator = typeof(CombGuidGenerator))]
        public Guid Id { get; set; }
        [BsonElement("Note")]
        public string Text { get; set; }
        [BsonElement("Date")]
        public DateTime Date
        {
            get { return date.ToLocalTime(); }
            set { date = value; }
        }
    }
 

Esta clase representa el documento que vamos a guardar en MongoDb, una nota que tiene un identificador único, una fecha y un texto.

Ahora, vamos a crear la clase Dal que nos ayudará a interactuar MongoDB. El constructor de esta clase recibe la cadena de conexión con Mongo y el nombre de la base de datos.

public class Dal : IDisposable
    {
        private MongoServer mongoServer = null;
        private bool disposed = false;
        private string collectionName = "Notes";
        private string connectionString;
        private string dbName;
        // Default constructor.
        public Dal(string strconnectionString, string strdbName)
        {
            connectionString = strconnectionString;
            dbName = strdbName;
        }
        # region IDisposable
        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }
        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    if (mongoServer != null)
                    {
                        this.mongoServer.Disconnect();
                    }
                }
            }
            this.disposed = true;
        }
        # endregion
    }

Paso 5: Conexión y consulta a MongoDB con objetos con tipo

La conexión a mongoDB se hace utilizando las clases MongoServer, MongoClient y MongoDatabase. El método ObtenerDB crea un cliente Mongo que se conecta al servidor y obtiene la referencia conectada a la base de datos.

private MongoDatabase ObtenerDB(string strdbName)
        {
            if (mongoServer == null)
            {
                var client = new MongoClient(connectionString);
                mongoServer = client.GetServer();

            }
            return mongoServer.GetDatabase(strdbName);
        }

Al tener la referencia a la base de datos ya conectada podemos comenzar a hacer consultas. El Método GetAllNotes() se conecta a la base de datos, obtiene la colección de documentos y los entrega en una lista. Aquí debemos notar que estamos usando una colección fuertemente tipada ya que la lista es del tipo <DocumentoNota>.

public List<DocumentoNota> GetAllNotes()
        {
            try
            {
                MongoDatabase database = this.ObtenerDB(this.dbName);
                MongoCollection<DocumentoNota> noteCollection =
                    database.GetCollection<DocumentoNota>(this.collectionName);
                return noteCollection.FindAll().ToList<DocumentoNota>();
            }
            catch (MongoConnectionException)
            {
                return new List<DocumentoNota>();
            }
        }

Para realizar la consulta vamos a utilizar un Web Form que llamaremos defaul.aspx. Para crearlo usamos el botón derecho opción Add New Form. Esto nos entrega una forma vacía a la cual agregamos una GridView llamada GridView1. Luego agregamos un botón que llamamos btActualizar.

Vamos a declarar una propiedad privada del tipo Dal llamada xMongo que utilizaremos en todos los métodos de este Web Form para interactuar con MongoDb. En el método Page_Load() creamos una instancia de xMongo leyendo desde la configuración del Cloud Services la cadena de conexión y el nombre de la base de datos de mongo. Por último, realizamos la consulta en el método ActualizarWebForm() que es el evento que se gatilla cuando se hace click en el botón  btActualizar. El siguiente código muestra lo implementado hasta ahora.

private Dal xMongo;
        private void ActualizarWebForm()
        {
            this.GridView1.DataSource = xMongo.GetAllNotes();
            GridView1.DataBind();
        }
        protected void Page_Load(object sender, EventArgs e)
        {
            if (xMongo == null)
            {
                xMongo =
new Dal(CloudConfigurationManager.GetSetting("mongoDB"), CloudConfigurationManager.GetSetting("dbName"));
            }
        }
        protected void btActualizar_Click(object sender, EventArgs e)
        {
            this.ActualizarWebForm();
        }

Antes de ejecutar nuestra primera consulta debemos ingresar en la configuración los valores de dbName y mongoDB, como se muestra en la siguiente imagen.

img6

Los valores a utilizar los debemos obtener desde el sitio de administración de Mongo LAB. En el panel de control del portal de Azure, en nuestra el ítem de MongoLab creado utilizando el botón de Connection info obtenemos la cadena de conexión de MongoDB. Luego vamos al portal de MongoLab y copiamos el nombre de la base de datos que viene creada por defecto. Para hacer esto, debemos apretar el botón Manage. La siguiente imagen nos muestra el portal de administración.

img7

Si ejecutamos nuestro proyecto, funciona pero no nos trae registros ya que no hemos ingresado dato alguno. Vamos a agregar un método CrearNota() como se muestra a continuación. Aquí recibimos un objeto de tipo Note y lo agregamos a la colección con el método insert.

public void CrearNota(DocumentoNota note)
        {
            MongoCollection<DocumentoNota> collection =this.ObtenerDB(this.dbName).GetCollection<DocumentoNota>(this.collectionName);
            try
            {
                collection.Insert(note, SafeMode.True);
            }
            catch (MongoCommandException ex)
            {
                string msg = ex.Message;
            }
        }

Por último en nuestro formulario Web agregamos un texbox y un botón para ingresar el texto de la nueva nota. Y utilizando el Dal podemos crear nuevas notas.

protected void btNuevaNota_Click(object sender, EventArgs e)
        {
            DocumentoNota valor = new DocumentoNota();
            valor.Date = DateTime.Now;
            valor.Text = tbTexto.Text;
            xMongo.CrearNota(valor);
            tbTexto.Text="";
            this.ActualizarWebForm();
        }

Ahora, ejecutamos nuestro proyecto e ingresamos algunas notas como muestra la siguiente figura.
img8

Paso 6: consulta a MongoDB con objetos sin tipo

Una de las características de Mongo es que puede manejar esquemas dinámicos en los documentos. Para poder aprovechar esta ventaja, no podemos usar objetos con tipos definidos en nuestra aplicación sino que debemos usar un tipo genérico llamado BsonDocument. Ahora, vamos a hacer la misma consulta que hicimos antes para obtener una lista de notas sin tipo, sino que la lista tendrá la lista de documentos en formato de objetos Json. Para esto le agregamos al Dal el siguiente método.

public List<string> GetAllElementosJson( string nombreColeccion)
        {
            var str = new List<string>();

            foreach (var item in this.ObtenerDB().GetCollection<MongoDB.Bson.BsonDocument>(nombreColeccion).FindAll())
            {
                str.Add(item.AsBsonDocument.ToString());
            }
            return str;
        }

Ahora, modificamos nuestro Web Form agregando un checkbox que nos permite seleccionar trabajar con o sin tipo. Actualizamos el método ActualizarWebForm() y podemos ya leer con o sin tipo como se muestra en el código actualizado. Nótese que le entregamos el nombre de la colección de documentos que queremos leer.

        private void ActualizarWebForm()
        {
            if (chbTipo.Checked)
            {
                this.GridView1.DataSource = xMongo.GetAllNotes();
            }
            else
            {
                this.GridView1.DataSource = xMongo.GetAllElementosJson("Notes");
            }
            GridView1.DataBind();
        }

Si desmarcamos el checkbox obtendremos la lista de objetos como muestra la imagen 9.img9

Paso 7: Actualizar Documentos en Mongo a partir de objetos sin tipo

Para crear una nueva nota en la colección, sin utilizar un objeto con tipo utilizamos nuevamente la clase   BsonDocument como muestra el método del Dal crearBsonDocumentNota().

public void crearBsonDocumentNota (string strTexto, string strColeccion)
{
   MongoDB.Bson.BsonDocument documentoX = new MongoDB.Bson.BsonDocument {
   { "_id", Guid.NewGuid() } ,
   { "Date", DateTime.Now.ToLocalTime() },
   { "Note", strTexto }
             };
    var collection =
        this.ObtenerDB(this.dbName).GetCollection<MongoDB.Bson.BsonDocument>(strColeccion);
    try
    {
      collection.Insert(documentoX);
     }
     catch (MongoCommandException ex)
     {
         string msg = ex.Message;
      }
  }

Ahora, actualizamos el método btNuevaNota_Click() para que dependiendo de la selecciona del checkbox agreguemos notas usando tipos o no.

        protected void btNuevaNota_Click(object sender, EventArgs e)
        {
            if (chbTipo.Checked)
            {
                DocumentoNota valor = new DocumentoNota();
                valor.Date = DateTime.Now;
                valor.Text = tbTexto.Text;
                xMongo.CrearNota(valor);
            }
            else
            {
                xMongo.crearBsonDocumentNota(tbTexto.Text,"Notes");
            }
            tbTexto.Text = "";
            this.ActualizarWebForm();
        }

Paso 8: Borra un documento de la colección

En este punto ya podemos crear y ver documentos de una colección, utilizando o no una clase con tipo. Ahora, vemos a borrar las notas de la colección.

public void borrarAllDocumentos(string strCcoleccion)
        {
            MongoCollection < MongoDB.Bson.BsonDocument > notesCollection =
                this.ObtenerDB(this.dbName).GetCollection <MongoDB.Bson.BsonDocument> (strCcoleccion);
            notesCollection.RemoveAll();
        }

Luego en el formulario Web agregamos un botón para borrar todos los documentos e invocamos este método. Al hacerlo vamos a borrar todos los documentos de la colección.

Paso 9: Representar la colección de documentos como un árbol

El paradigma de las bases de datos documentales como mongoDB es tener colecciones de documentos que a su vez pueden contener colecciones de documentos como un atributo. Esto se representa de manera natural en una estructura de árbol más que en una tabla. Las tablas son la representación natural del paradigma tradicional/relacional por excelencia.

Ahora vamos a implementar el mapeo desde una colección de MongoDB hacia un TreeNode para usarlo como fuente de datos del control Web Treeview. Para esto vamos a utilizar recursividad, que debemos recorrer el árbol desde la raíz hasta las hojas de cada rama.

La raíz del árbol se crea en el método getArbol(string strColeccion)  y obtiene la colección de documentos. Aquí se recorre todos los elementos que tiene en este primer nivel la colección de documentos del tipo BsonDocument. Este documento puede contener un arreglo de documentos, un documento con alguna estructura definida o un elemento dentro del documento.

El método obtnerNodo() es quien discrimina el tipo de documento que se está procesando y determina si es una hoja del árbol por lo cual debe ser procesada, un documento o un arreglo de documentos. Los dos últimos casos, que en el Switch del código son MongoDB.Bson.BsonType.Array y MongoDB.Bson.BsonType.Document,  lo que hacen es llamar de manera recursiva al mismo método obtenerNodo() hasta llegar al nivel de la hoja. En ese momento lo que hace el método es agregarlo como un nodo al TreeNode. Ambos métodos se muestran en el siguiente código.

private TreeNode obtnerNodo(MongoDB.Bson.BsonDocument doc, TreeNode hoja)
        {
            foreach (var item in doc.Elements)
            {
                switch (item.Value.BsonType)
                {
                    case MongoDB.Bson.BsonType.Array:
                        var hojaHijaArray = new TreeNode(item.Name);
                        foreach (var documento in item.Value.AsBsonArray)
                        {
                            this.obtnerNodo(documento.AsBsonDocument, hojaHijaArray);
                        }
                        hoja.ChildNodes.Add(hojaHijaArray);
                        break;
                    case MongoDB.Bson.BsonType.Document:
                        var hojaHijaDoc = new TreeNode(item.Name);
                        this.obtnerNodo(item.Value.AsBsonDocument, hojaHijaDoc);
                        hoja.ChildNodes.Add(hojaHijaDoc);
                        break;
                    default:
                        var etiqueta = string.Format("{0}: {1} [{2}]", item.Name, item.Value.RawValue.ToString(), item.Value.BsonType.ToString());
                        hoja.ChildNodes.Add(new TreeNode(etiqueta));
                        break;
                }
            }
            return hoja;
        }
        public TreeNode getArbol(string strColeccion)
        {
            var arbol = new TreeNode(strColeccion);
            int acc = 0;
            foreach (var item in this.ObtenerDB(this.dbName).GetCollection<MongoDB.Bson.BsonDocument>(strColeccion).FindAll())
            {
                arbol.ChildNodes.Add(this.obtnerNodo(item, new TreeNode(acc.ToString())));
                acc += 1;
            }
            return arbol;
        }

El resultado de llamar a getArbol() es un objeto TreeNode que representa la estructura de la colección en un árbol con ramas y hojas, donde las hojas son los valores de las propiedades de los documentos. Para verlo funcionando, ampliamos el método ActualizarWebForm() para que además de mostrar la tabla con valores de la colección, muestre un árbol con la misma.

private void ActualizarWebForm()
        {
            if (chbTipo.Checked)
            {
                this.GridView1.DataSource = xMongo.GetAllNotes();
            }
            else
            {
                this.GridView1.DataSource = xMongo.GetAllElementosJson("Notes");
            }
            tvArbol.Nodes.Clear();
            tvArbol.Nodes.Add(xMongo.getArbol("Notes"));
            GridView1.DataBind();
        }

Ahora, cuando ejecutamos la aplicación de ejemplo y actualizamos vemos tanto los datos en la tabla como en el árbol. Nótese que no estamos usando el método con objetos con tipo definido, ya que la idea es que el mapeo nos sirva para cualquier colección.
img10

Paso 10: Representar una  colección más compleja de documentos como un árbol

Para hacer más ilustrativo el ejemplo, y representar mejor la potencia de los esquemas dinámicos y la posibilidad de contener documentos dentro de documentos, vamos a implementar una segunda colección que sea una lista de las notas ingresadas en la colección Notes.

En la clase Dal vamos a agregar un método que agrupará las notas de la colección Notes en una nueva colección de documentos, el cual tendrá un par de atributos y un elemento llamado “Notas”. Este será una colección de los documentos de las notas ingresadas. Con esto vamos a ver una colección que tiene un documento (raíz) que tiene a su vez en su contenido una colección de documentos. En Dal agregamos el siguiente método.

  public string AgruparNotas(string strColeccionAgrupacion)
        {
            MongoDB.Bson.BsonArray Libro = new MongoDB.Bson.BsonArray();
            var pagina = new MongoDB.Bson.BsonDocument();
            pagina.Add("_id", Guid.NewGuid());
            pagina.Add("cierre", DateTime.Now.ToLocalTime());
            int acc = 0;

            var notas = new MongoDB.Bson.BsonArray();
            foreach (var item in this.ObtenerDB(this.dbName).GetCollection<MongoDB.Bson.BsonDocument>(this.collectionName).FindAll())
            {
                var nota = new MongoDB.Bson.BsonDocument();
                nota.Add("Registro", acc.ToString());
                nota.Add("Nota", item.AsBsonDocument);
                notas.Add(nota);
                acc += 1;
            }
            pagina.Add("Notas", notas);
            Libro.Add(pagina);
            var collection = this.ObtenerDB(this.dbName).GetCollection<MongoDB.Bson.BsonArray>(strColeccionAgrupacion);
            collection.InsertBatch(Libro);

            return Libro[0].AsBsonDocument.ToString();
        }

Ahora, en la interfaz Web volvemos a modificar el método ActualizarWebForm() para que ahora al momento de actualizar el formulario, cree esta nueva colección y la muestre en el Treeview.

private void ActualizarWebForm()
        {
            if (chbTipo.Checked)
            {
                this.GridView1.DataSource = xMongo.GetAllNotes();
            }
            else
            {
                this.GridView1.DataSource = xMongo.GetAllElementosJson("Notes");
            }
            tvArbol.Nodes.Clear();
            xMongo.borrarAllDocumentos("agrupacion");
            xMongo.AgruparNotas("agrupacion");
            tvArbol.Nodes.Add(xMongo.getArbol("agrupacion"));
            GridView1.DataBind();
        }

Una vez hecho esto, el resultado será que el árbol ahora muestra los diferentes niveles. Los documentos de la colección Notes ahora son parte del documento de la colección agrupación como aparece en la imagen 11.img11

El código fuente esta aquí.

Conclusión

En este pequeño artículo podemos ver cómo utilizar MongoDb de manera muy básica y comenzar a comprender el paradigma de las bases de datos documentales. Además, vemos como MongoDb está ya disponible en el modelo DBaaS en Windows Azure y como una aplicación Cloud Services del tipo Web puede utilizarla.

Links Relacionados