miércoles, 25 de agosto de 2010

Utilización de Tópicos (Topics)

¿Que es un Tópico (Topic)?

Un Tópico es una herramienta de publicación de mensajes a subscriptores, esto quiere decir que cada vez que yo ponga un mensaje en un Tópico todos los subscriptores lo van a recibir.

En general se lo relaciona directamente con las Colas por ser un caso especial de las mismas, o viceversa (para evitar discusiones filosóficas).

Los Tópicos resuelven dos grandes problemas de las arquitectura:
  • Publicación
  • Replicación
  • Desacoplamiento
Publicación
En ambientes distribuidos muchas veces es necesario transmitir comandos a todos los nodos. Por ejemplo comandos para reinicializar caches y estadísticas, etc.

Replicación
Cuando uno necesita tener varios ambientes replicados. Esto quiere decir que pongo datos en el Tópico que necesito si inserte en varios ambientes. Es muy útil en entornos heterogéneos donde puedo tener una base de datos Oracle y otra DB2.

Desacoplamiento
Al igual que las Colas los Tópicos son un intermediario por definición, el desacoplamiento es totalmente natural y se pueden agregar y sacar suscriptores en cualquier momento.

lunes, 16 de agosto de 2010

Utilización de Colas (Queue) (Uso de Colas)

¿Qué es una Cola (Queue)?

En la actualidad las colas son la herramienta por excelencia para cumplir con tres conceptos fundamentales de arquitectura:
  • Distribución de carga
  • Desacoplamiento
  • Transaccionabilidad y Tolerancia
Distribución de carga
Las Colas tienen tres características básicas que favorecen su participación en la distribución de carga:
  • Múltiples clientes simultáneos, varios clientes (Agentes) pueden estar esperando mensajes que se depositen la cola al mismo tiempo. Estos Agentes pueden estar en el mismo servidor o en distintos servidores.

  • Exclusividad de mensajes, un mensaje que es tomado (decolado) por uno de los Agentes es bloqueado para que no sea decolado por otro Agente.

  • Centralización de la distribución, al poner la cola en un servidor central es muy fácil su administración así como también agregar agentes o quitarlos.
Desacoplamiento
Al ser la Cola un intermediario por definición, existe una separación entre el proveedor del mensaje y el consumidor del mismo. El único requisito es que se sepa manejar el mensaje.

Transaccionabilidad y Tolerancia
Una de las características transaccionales de las colas es que si uno de los agentes fracasa en su ejecución el mensaje es devuelto a la cola para ser procesado por otro agente que tal vez tenga éxito.

El rollback puede darse por dos razones:
  • Voluntaria, el programador lo genera porque ha detectado que hay un error que puede ser reintentado, casos como IO (Sin espacio en disco, conexión caída, etc.).

  • Involuntaria, se cayo el servidor, problema de hardware, etc.

domingo, 3 de mayo de 2009

Implementación de ASP.NET MVC para .NET 2.0

En uno de los trabajos freelance web que me ha tocado realizar uno de los requierimientos fue que corriera sobre ASP.NET 2.0, y por error yo realice todo el trabajo sobre ASP.NET MVC y sobre .NET Framework 3.5. Al darme cuenta del error trate de buscar una forma de hacer correr el codigo igualmente pero sin suerte.

Mas alla de los cambios obios que hay que realizar al codigo por la version del CLR y el interpretador de aspx, no encontre ninguna implementacion que soportara el modelo de vistas y controladores tal como la hace ASP.NET MVC.

Por esta razon he realizado la siguiente implementacion que por el momento soporta:

Controllers
Views
Actions

La base de todo esta en la configuracion del archivo web.config, ahi es donde todo empieza a funcionar, donde tenemos que imitar el funcionamiento de ASP.NET MVC.

Web.Config:
<httpHandlers> <add verb="*" path="/Encuestas/*" type="SistemaDeEncuestas.MainController"/>
<add verb="*" path="/Encuestas/*/*" type="SistemaDeEncuestas.MainController"/>
<remove verb="*" path="/Encuestas/Views/*"/> <remove verb="*" path="/Encuestas/Views/*/*"/>
<remove verb="*" path="/Encuestas/Content/*"/> <remove verb="*" path="/Encuestas/Content/*/*"/>
</httpHandlers>


Como bien saben ASP.NET MVC utiliza la url para hacer el mapeo contra los metodos que se encuentran dentro de nuestros controladores que tambien se mapean desde la url:

/MiAplicacion/"Controller"/""

Dentro de ASP.NET MVC a todo metodo que se accede de esta manera se lo llama Action.

Esta implementacion realiza todo este mapeo a traves de la clase MainController que uds pueden ver configurada como httpHanddler en el archivo Web.config.

MainController:
namespace MiAplicacion
{
public class MainController : IHttpHandler, IRequiresSessionState
{
public void ProcessRequest(HttpContext context)
{
// Parseo de la URL
string[] parts = context.Request.FilePath.Split('/');
if( parts.Length <>
return;
}
string applicationName = parts[1];
string actionName = parts[2];
string methodName = "Index";
if (parts.Length > 3)
{
methodName = parts[3];
}

if (actionName == null)
{
return;
}

ViewPage.setContext( context, applicationName, actionName );

// Instanciacion del Controller
string controllerName = this.GetType().Namespace+"."+actionName + "Controller";
Type t = Type.GetType(controllerName);
object controller = t.InvokeMember(controllerName, BindingFlags.CreateInstance, null, null, null);

t.InvokeMember("setApplicationName", BindingFlags.InvokeMethod, null, controller, new object[] { applicationName });

t.InvokeMember("setActionName", BindingFlags.InvokeMethod, null, controller, new object[] { actionName });

t.InvokeMember("setMethodName", BindingFlags.InvokeMethod, null, controller, new object[] { methodName });

t.InvokeMember("setHttpContext", BindingFlags.InvokeMethod, null, controller, new object[] { context });

// Ejecucion del Action (Metodo dentro del controller)
ActionResult actionResult = ((Controller)controller).ExecuteAction( methodName, context);

actionResult.ExecuteResult( new ControllerContext(context) );
}
public bool IsReusable
{
get
{
return false;
}
}
}
}


Como pueden ver para poder lograr todo esto se hace uso exaustivo de reflexion y instrocpection que para quienes no estan familiarizados con los terminos son formas de manipular las clases.

Para dar soporte a los elementos que se usan en ASP.NET MVC tuve que realizar la implementacion completa de las siguientes clases:
  • ActionResult
  • Controller
  • ControllerContext
  • Html
  • Url
  • ViewActionResult
  • ViewPage
La clase ViewPage posiblemente es la mas oculta pero definitivamente la mas util en la presentación ya que es la provee a la pagina aspx de los elementos:
  • ViewData
  • Html
  • Url
ViewPage:
public class ViewPage : System.Web.UI.Page
{
public Hashtable ViewData;
public static Url Url;
public static Html Html;
private static HttpContext context;

protected void Page_Load(object sender, EventArgs e)
{
if (Context.Items["ViewData"] != null)
{
ViewData = (Hashtable)Context.Items["ViewData"];
}
}

public ViewPage()
{
ViewData = new Hashtable();
}

public static void setContext( HttpContext httpContext, string applicationName, string actionName )
{
context = httpContext;
Url = new Url( applicationName, actionName);
Html = new Html(context);
}
}


Como pueden ver en el metodo Page_Load se asigan el objeto ViewData al item del contexto tambien llamado ViewData. Este item del contexto se asigna en la clase abstracta Controller, la cual es heredada por todos los controladores de nuestra aplicacion, cuando se ejecuta la llamada al metodo setHttpContext por reflexion en la clase MainContext en la linea 37.

Controller:

public abstract class Controller
{
public string applicationName;
public string actionName;
public string methodName;
public HttpContext httpContext;
public Hashtable ViewData;
public HttpSessionState Session { get { return httpContext.Session; } }
public HttpRequest Request { get { return httpContext.Request; } }

public Controller()
{
ViewData = new Hashtable();
}

public void setHttpContext(HttpContext httpContext)
{
this.httpContext = httpContext;
this.httpContext.Items["ViewData"] = ViewData;
}

public void setMethodName(string methodName)
{
this.methodName = methodName;
}

public void setApplicationName(string s)
{
this.applicationName = s;
}

public void setActionName(string s)
{
this.actionName = s;
}

public ActionResult View( string view )
{
if (ViewData["error"] != null)
{
httpContext.Items["error"] = ViewData["error"];
}
ViewActionResult action = new ViewActionResult();
action.setUrl("/" + applicationName + "/Views/" + actionName + "/" + view + ".aspx");
return action;
}

public ActionResult View()
{
return View( methodName );
}

public ActionResult RedirectToAction(string method )
{
methodName = method;
return RedirectToAction(method, null);
}

public ActionResult RedirectToAction( string method, object[] args )
{
methodName = method;
return ExecuteAction( method, args);
}

public ActionResult ExecuteAction(string method, object[] args)
{
methodName = method;
Type t = this.GetType();

ParameterInfo[] methodParams = t.GetMethod(method).GetParameters();
int methodParamsCount = methodParams.Length;

string[] keys = null;
object[] values = null;

if (methodParamsCount > 0)
{
keys = new string[methodParamsCount];
values = new string[methodParamsCount];

int i = 0;
foreach (ParameterInfo key in methodParams)
{
keys[i] = key.Name;
if (i < methodname =" method;" t =" this.GetType();" values =" null;" keys =" null;" methodparams =" t.GetMethod(method).GetParameters();" methodparamscount =" methodParams.Length;"> 0)
{
keys = new string[methodParamsCount];
values = new string[methodParamsCount];
int i = 0;
foreach (ParameterInfo key in methodParams)
{
keys[i] = key.Name;

if (context.Request.Form[key.Name] != null)
{
values[i] = context.Request.Form[key.Name];
}
else if (context.Request.QueryString[key.Name] != null)
{
values[i] = context.Request.QueryString[key.Name];
}
else
{
values[i] = null;
}
i++;
}
}
return (ActionResult)t.InvokeMember(method, BindingFlags.InvokeMethod, null, this, values, null, null, keys);
}
}


Como pueden ver en la clase Controller es donde se implementan los metodos:
  • View y View(viewName)
  • RedirectToAction(method) y RedirectToAction(method, args)
y se definen los objetos:
  • ViewData
  • Session
  • Request
Estos elementos son usados dentro de nuestro controlador:

SistemaController:

namespace SistemaDeEncuestas
{
public class SistemaController : Controller
{
public ActionResult Index()
{
if (Session["LoggedIn"] == null || (bool)Session["LoggedIn"] != true)
{
ViewData["error"] = "Primero de ingresar el sistema con sus credenciales";
return View("Login");
}
else
{
return View("Panel");
}
}
}
}

Bueno, por ultimo les dejo algunos tips para downgrade:
  • En los aspx se debe cambiar la definicion CodeBehind por CodeFile
  • No se pueden usar los elementos ASP.NET MVC como ViewData en los master page files.
  • Todo lugar donde alla usado new { key = value} debe reemplazarce por new object{key = value}
  • Para evitar cualquier inconveniente con los tipos de los datos yo use todos los parametros de los actions como strings y luego dentro los convierto al tipo correspondiente.
  • Tienen que reorganizar todos los usings porque ya no seran todos validos
Proximamente voy a subir todo el codigo a algun sitio para que lo puedan descargar y puedan seguir completando lo que falta para tener un soporte completo de todas las clases.

El codigo completo se puede bajar desde aqui:

http://aspnetmvc20.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=26921