viernes, 14 de abril de 2017

Creación de Cluster de Linux – CentOS 7

Introducción

Este post tiene la finalidad de mostrar como crear un Cluster con dos servidores de Linux, utilzando el sistema operativo CentOS y Pacemaker.
  • El Cluster trabajará de la siguiente manera:
  • Cada servidor de Linux representa un Nodo en el cluster.
  • Los nodos del Cluster compartirán una IPVirtual, la cual es el primer recurso que se configura en el Cluster.
  • El nodo Activo será el primer servidor que se logre levantar.
  • Si el Nodo que tiene asignada la IPVirtual deja de responder entonces el otro Nodo tomara posesión de la IPVitual.
  • Solo se pude intercambiar la IPVirtual si el Nodo que tiene la posesión deja de responder, ya sea haya sido desactivando, apagando o por medio de una falla de red en el Nodo.

Instalación y Configuración de los Servidores

Este ejemplo utiliza es ejemplificado por medio de maquinas virtuales, utilizando en VMWare Fusion. Las características de los servidores y datos de las cuentas que serán utilizadas son:

  • Versión de Fedora: Centos 7 (x86_64 Bits)
  • Maquina virtual con:
  1. 2 cores
  2. 10 GB de DD
  3. 1.2 GB de RAM

Usuario de administración root
  • pwd: *********

Usuario de Middleware, con privilegios de administrador.
  • user: srvmdlw
  • pwd: **********

En este caso de nombra a la entidad Middleware, pensando que el Cluster será una capa intermedia para acceder a diversos servicios.
Nodo1
  • Hostname: mddlwr.n1
  • IP PUB: 192.168.13.231
  • MAC ADDRESS1: 00:50:56:31:5B:AA

Nodo 2
  • Hostname: mddlwr.n2
  • IP PUB: 192.168.13.232
  • MAC ADDRESS1: 00:50:56:3E:FA:54

Nodo Middleware, quien representa al Cluster.
  • IP Virtual: 192.168.13.230
  • User: hacluster
  • PWD: *********
  • ClusterName: mddlwr.cluste

Instalación y Configuración de Linux CENTOS 7

Los paquetes que se deben instalar en cada uno e las instalaciones de Linux.

Asignación de IP’s

Utilizar los datos de acuerdo al nodo del Middleware que se esté instalando (Nodo 1 o Nodo 2), descritos en la sección Datos de Instalación.
IP Pública
IP Privada

Instalación del Cluster

El comando para instalar los paquetes de High Availability debe ser realizado como usuario ROOT, ademas de tener los servidores conectados a Internet.
Instalar el paquete de Corosync
# yum -y install corosync pacemaker pcs
Si ya se tiene instalada la última actualización, se obtendrá el siguiente resultado:

Abrir puertos de Firewall

Abrir puertos UDP 5404 y 5405, para Corosync:
# iptables -I INPUT -m state --state NEW -p udp -m multiport --dports 5404,5405 -j ACCEPT
Abrir TCP 2224, para PCS:
# firewall-cmd --permanent --add-port=2224/tcp
Permitir el tráfico IGMP:
# iptables -I INPUT -p igmp -j ACCEPT
Permitir el tráfico MULTICAST:
# iptables -I INPUT -m addrtype --dst-type MULTICAST -j ACCEPT
Antes de guardar los cambios, asegúrese de actualizar el servicio de iptable.
# yum install iptables-services
Guardar los cambios hechos:
# service iptables save
iptables: Saving firewall rules to /etc/sysconfig/iptables:[ OK ]
Para revisar las reglas de Firewall, utilice el siguiente comando:
# iptables -L -n

Preparando tabla de Hostnames

Para todos los nodos, se encontrarán por medio del host name y la IP privada, por lo que la edición debe ser en el archivo de Hosts, en ambos nodos.

Cambiar password del usuario hacluster

Ejecutar el comando en ambos nodos.
El usuario ya debería estar creado, el uso de este comando es para confirmar que está creado:
# useradd hacluster
Asignación de password
#passwd hacluster
Los datos del password están descritos en la sección Datos de Instalación.

Iniciar servicio PCS

Ejecutar el comando en ambos nodos.
# systemctl enable pcsd.service
# systemctl start pcsd.service

# systemctl enable corosync.service
# systemctl start corosync.service

# systemctl enable pacemaker.service
# systemctl start pacemaker.service

Autorizando los servidores

Ejecutar el comando en ambos nodos.
# pcs cluster auth mddlwr.n1 mddlwr.n2
El comando le solicitará identificarse como el usuario hacluster.  El resultado debe indicarse que se logró identificar en ambos nodos.

Creación de cluster con sus nodos

Ejecutar este comando en el nodo 1.
# pcs cluster setup --name mddlwr.cluster mddlwr.n1 mddlwr.n2
Este comando creará el archivo /etc/corosync.conf, el cual contendrá la configuración de clúster.
Después de la creación se puede iniciar el clúster, aunque todavía falte configurarlo. Para iniciarlo se utiliza el comando:
# pcs cluster start --all
Este comando iniciará los nodos del cluster:

Revisando el estado del Clúster

Para revisar el estado del Clúster se debe utilizar el siguiente comando:
# pcs status cluster
En este caso todavía no se tiene configurado algún recurso:
2 nodes and 0 resources configured
Online: [ mddlwr.n1 mddlwr.n2 ]
Para revisar el estado de los nodos se utiliza el siguiente comando:
# pcs status nodes
Para revisar el estado de los miembros del cluster:
# corosync-cmapctl | grep members
Para obtener información del grupo:
# pcs status corosync
Para obtener información completa del cluster y sus servicios:
# pcs status

Configuración del Cluster

Se debe utilizar el usuario root para ejecutar lo comandos de esta sección y deben ser ejecutados en ambos nodos, caso de este ejemplo.
Antes de iniciar se revisa si se tiene errores configuración en el cluster, por lo que se utiliza el comando:
# crm_verify -L -V
El cual nos muestra una lista de errores, en donde se indica que no tiene configurado la propiedad STONITH, el cual es un mecanismos que asegura que no se tengan dos nodos activos, lo que provocaría que se estaría compitiendo por la IP compartida.
En este caso se tiene una configuración sencilla de cluster, por lo que deshabilitamos esta propiedad con el siguiente comando:
# pcs property set stonith-enabled=false
Sobre la configuración del cluster, se tiene una propiedad que indica el quórum mínimo requerido para que el Cluster esté operando. Esta propiedad es útil en el caso de que se tengan varios nodos; pero en el caso de nuestro cluster solo se cuenta con dos nodos, por lo que es recomendable que se desactive esta propiedad, pues la cantidad mínima requerida de nodos levantados es de un (1) nodo.
El comando para ignorar el umbral de nodos mínimos es:
# pcs property set no-quorum-policy=ignore
Para revisar los cambios hechos, STONITH y mínimo quórum, utilice el siguiente comando:
# pcs property

Asignación de IP Virtual

Una vez configurado los aspectos previos del Cluster, se debe asignar l IP que compartirán en común los nodos.
En este caso, del cluster, se considera que la creación de la IP Virtual como un nuevo recurso, por lo que se crea un nuevo recurso con un tiempo de 15 segundos de poleo. El tiempo de poleo se ajusta de acuerdo a sus necesidades.
# pcs resource create virtual_ip ocf:heartbeat:IPaddr2 ip=192.168.13.230 cidr_netmask=32 op monitor interval=15s
En este ejemplo se ejecuto el comando desde el nodo 1, creando el recurso en ambos nodos.
Para revisar la configuración de los recursos se utiliza el siguiente comando:
# pcs status resources
Ahora la IP Virtual ya puede ser alcanzada por otro dispositivo dentro de la red:

Activando / Suspendiendo un Nodo del Cluster

Como procedimiento opcional, se puede desactivar o activar nodos con los siguientes comandos:
Desactivar un Nodo, en este ejemplo es el nodo 2.
# pcs cluster unstandby mddlwr.n2
Activar un Nodo, en este ejemplo es el nodo 2.
# pcs cluster standby mddlwr.n2
De esta manera se puede desactivar un nodo para que el otro se quede activo con la IP Virtual, dato que se puede consultar al consultar el estatus del cluster:

Implementando HTTP Server de Apache como un recurso del Cluster

Firmado  con super usuario ROOT
En este caso implementaremos el servicio de Apache HTTP para ser parte de los recursos del cluster, de tal manera que si se llegase a detener el servicio httpd, se active el siguiente nodo.
Instalación del software de Apache:
# yum install httpd
Abriendo en el Firiwall el Puerto 80:
# firewall-cmd --permanent --add-port=80/tcp
# firewall-cmd --reload
Habilitar servicio
# systemctl enable httpd
Detener el servicio, si se encuentra ejecutándose
# systemctl stop httpd
Creamos el mecanismo que permitirá al cluster monitorear el http server. Este mecanismo consiste en un archivo de configuración, llamada serverstatus.conf, ubicado en la ruta /etc/httpd/conf.d, con el siguiente contenido:
Listen 127.0.0.1:80

SetHandler server-status
Order deny,allow
Deny from all
Allow from 127.0.0.1

Este archivo debe estar contenido en ambos nodos, o los que formen parte del cluster.
Ahora se debe comentar, del archivo de configuración del http server, la configuración correspondiente al Listener, para evitar que se trate de escuchas en múltiple ocasiones sobre el mismo puerto. Esta paso se lleva a cabo con el sigueinte comando:
# sed -i ‘s/Listen/#Listen/’ /etc/httpd/conf/httpd.conf
Antes del comando:
Después del comando:
Iniciamos el servidor http, en ambos nodos:
# systemctl start httpd
Debido a que se tiene desactivado el Listener, no será posible realizar peticiones al servidor http. La manera de ver que este trabajando el http server es po comando, realizando una petición al localhost, solicitando el estado del servidor, mecanismo que se implemento con el archivo serverstatus.conf:
# wget http://127.0.0.1/server-status
Esta validación se debe llevar a cabo en ambos nodos.
El siguiente paso es el de crear en el http server un Listener hacia el puerto 80 y utilizando la IP Virtual. Para esto se modificará el archivo de configuración del http server, en ambos nodos, utilizando los siguientes comandos, en ambos nodos:
# echo "Listen 192.168.13.230:80"|tee --append /etc/httpd/conf/httpd.conf
Antes del comando:
Después del comando:
Reiniciar el servicio de http server. Una vez reiniciado el servicio será posible realizar peticiones desde equipos externos.
Una vez que se tiene este mecanismo/interfaz, el cluster tendrá la capacidad de monitorear y controlar el servidor http. Por lo que el siguiente paso es incluir el servidor http como un recurso más del cluster, utilizando el siguiente comando en cualquiera de los nodos disponibles del cluster:
# pcs resource create webserver ocf:heartbeat:apache configfile=/etc/httpd/conf/httpd.conf 
statusurl="http://localhost/server-status" op monitor interval=25s
Antes de ejecutar el comando se puede consultar el estado del cluster y apreciar que se encuentra configurado un solo recurso:
Después de la ejecución se pueden apreciar el nuevo recurso:
En este punto el cluster solo puede asignarle la IP a un solo nodo, por lo que al tratar de levantar el http server al nodo no activo se tendrá el error de que no se pudo asociar el puerto a la IP establecida en la configuración. Esta situación se debe arreglar configurando una restricción en el cluster, utilizando el siguiente comando:
# pcs constraint colocation add webserver virtual_ip INFINITY
Este comando indica que los recursos dependen de la IP Virtual, pero ademas se requiere evitar que el webserver inicie antes que se asigne una IP Virtual. Para esto se debe crear otra restricción, la cual indica cuál debe ser el orden de arranque de los recursos:
# pcs constraint order virtual_ip then webserver
Para revisar los recursos configurados hasta el momento, utilice el comando:
# pcs resource
Para revisar las restricciones configuradas hasta el momento, utilice el comando:
# pcs constraint
Pare que inicie por completo el cluster:
Revise el estado del cluster en ambos nodos:
Nodo 1:
Nodo 2:
En ambos nodos se aprecia que el Cluster está operando exitosamente, así como también se aprecia que los recursos están trabajando en el nodo 1:

Instalando Tomcat

Versión de Tomcat: 9.1
Ruta de instalación de Tomcat:
/home/srvmdlw/tomcat
Ruta de instalación de JRE:
/lib/jvm/jre
Abrir puertos 8080:
 firewall-cmd --permanent --add-port=8080/tcp
 firewall-cmd --reload
Crear recurso de Tomcat en Pacemaker:
# pcs resource create tomcat ocf:heartbeat:tomcat params java_home=/lib/jvm/jre catalina_home=/home/srvmdlw/tomcat op monitor interval=10s
La restricción para que arranque después de la IP Virtual:
# pcs constraint colocation add webserver virtual_ip INFINITY
# pcs constraint order virtual_ip then webserver

# pcs constraint colocation add tomcat virtual_ip INFINITY
# pcs constraint order webserver then tomcat
Para conocer el Id de los Constrains:
# pcs constraint list --full
Para remover la restricción anterior:
# pcs constraint order remove virtual_ip webserver
Asignar el nuevo orden de arranque:
# pcs constraint order virtual_ip then webserver
# pcs constraint order webserver then tomcat

Procesos para evaluar el funcionamiento del Cluster

Recomiendo utilizar los siguientes comandos para revisar el funcionamiento del Cluster:
Estado General:
#pcs status
Estado del Cluster:
#pcs status cluster
Estado de los Recursos:
#pcs status resource
Los logs del CoroSync se encuentran en la siguiente ruta:
/var/log/cluster/corosync.log

Referencias

Guía para crear un Cluster sencillo:
Monitoreo de Apache:
Manual de Pacemaker:
Configuración de Tomcat para PCS

lunes, 10 de abril de 2017

Uso de Sockets con Alto Desempeño (High Performance)

Recientemente me tope con un problema que desafío mi manera de atender multiples conexiones por  Sockets (Mi Paradigma),  en este caso C#; y es que ese fue el problema principal: Usar un paradigma sin considerar la carga de trabajo que recibiría el servicio.

El Entorno de Trabajo


  • La aplicación es un Windows Service, hecho en C#, el cual levanta un Socket Server para atender multiples peticiones (Requests) de manera simultánea, por medio de TCP.
  • Cada Request implica leer el comando de entrada, el cual tiene un tamaño fijo de 612 bytes.
  • Una vez que se haya terminado de atender el Request se procede a enviar la respuesta (Response), con un tamaño fijo de 612 bytes.
  • Una vez que se termino de enviar el response se debe cerrar la conexión de socket.
  • En hora pico se tiene una carga promedio de 15 mil Requests por Hora.

Mi Paradigma

El paradigma que utilizaba para atender cada una de las peticiones era:


  1. Crear un Thread para levantar un Socket Server.
  2. Por cada petición de conexión que recibía el Socket Server se crea un nuevo Thread para atender la petición del Socket Client.
  3. Dentro del nuevo Thread se procede a leer los datos que envío el Socket Cliente (Request).
  4. Una vez que se tiene el Request se procede a trabajarlo dentro del mismo thread.
  5. Una vez que se haya procesado el Request se envía la respuesta (Response).
  6. Cuando se haya terminado de enviar el Response se cierra el Socket.
  7. Una vez terminado el envío del Response se cierra el Socket.


El problema que tiene este paradigma es que bajo una carga de trabajo, como la que se describe en la sección "El Entorno de Trabajo" es que se presento una degradación en la atención de las peticiones y el servicio, presentando los siguientes sintomas:


  • Retraso de hasta 40 segundos para recibir y responder una petición.
  • Consumo de procesador de hasta 50%, antes de colapsar la atención de los Request.
  • Las aplicaciones que realizaban la operación de Socket Client, no recibían la respuesta a tiempo y generaban una excepción de TimeOut en la lectura del Response.

Considere que la atención de la petición puede llevar al Thread a esperar, por medio de los metodos Sleep y Wait.

La Solución


Después de batallar por algunos días, la respuesta al dilema fue atender las peticiones de manera Asíncrona y no por medio de un nuevo Thread dedicado.

El crear un Thread para atender una petición de Socket es una solución viable, siempre y cuando la carga de trabajo y el tiempo de respuesta no seam factores determinantes en el desempeño de tu aplicación. Pero en el caso de una aplicación de Alto Desempeño, en el que se requiere atender varias peticiones simultáneas en el menor tiempo posible, no es una solución que pueda funcionar.

Para el caso de C# se requiere del uso de BeginRead, EndRead, BeginWrite y EndWrite, de NetworkStream. Estos métodos permiten al Socket Clientes, en conjunto con el Socket Server, de recibir la y enviar datos de manera asincrónica, sin necesidad de crear un nuevo Thread.

La implementación fue la siguiente:


  1. Se conserva el Thread dedicado a aceptar las peticiones de conexión de los clientes. 
  2. Por cada petición de conexión se creará un nuevo objeto Request, asignándole el Socket Client que se encargará de atender la comunicación Client-Server.
  3. El objeto Request será el encargado de obtener el Request, procesarlo y enviar el Response, de manera asincrónica.
  4. El proceso de recepción inicia con la invocación del método Request::startReceiveAsync.
  5. El método startReceiveAsync invoca al método BeginRead, de NetworkStream, especificando cuantos bytes debe leer antes de invocar el método de retroalimentación Request::requestReceived, el cual se especifica en el método BeginRead.
  6. Al momento de invocar BeginRead se continua con la ejecución del programa sin esperar a que se haya recibido el Request, permitiendo con esto terminar la ejecución del método startReceiveAsync y regresando el CallStack al punto en donde se espera una nueva conexión de socket.
  7. Una vez que es invocado el método requestReceived, que es cuando se termino de leer los datos,  es importante invocar el método EndRead, del NetworkStream, para asegurarse de que se haya terminado de recibir toda la cantidad de datos solicitados en el método BeginRead. Se recomiendo utilizar EndRead en el caso de que se espera recibir grandes cantidades de información.

La secuencia de invocación entre de los métodos startReceiveAsyncrequestReceived se lleva a cabo de manera asincrónica, pues el objeto NetworkStream se encarga de invocarlos a medida que los datos se encuentren disponibles para su lectura. Todo esto sin tener que utilizar un bloqueo en los sockets o en el Thread de atención al cliente.

El código para aceptar la conexión y recepción del Request es:



 

/* Metodo Server::run()*/
public void run()
{
    server = new TcpListener(IPAddress.Any, port);
    server.Start();
    
    log.Info("Iniciando espera de peticiones en el puerto [" + port + "]");

    while (keepRunning)
    {
        try
        {
            TcpClient socket = server.AcceptTcpClient();
            if (keepRunning)
                RequestManager.createRequestForEvalue(socket, idLayout);
        }
        catch (Exception ex)
        {
            log.Error("Se detecto un error en el puerto para atencion de peticiones. ERR: " + ex.Message);
            log.Error(ex.StackTrace);
        }
    }

    log.Info("Servicio deteniendo.");
}


/* Metodo RequestManager.createRequestForEvalue*/
public static bool createRequestForEvalue(TcpClient socket, int idLayout)
{
    Request req = null;
    req = new Request(socket,idLayout);

    registerRequest(req.ID,req); //Registra el Request, para su posterior uso.

    req.startReceiveAsync(); //Since V4.20
    return true;
}


/*Metodo Request.startReceiveAsync*/
public void startReceiveAsync()
{
    try
    {
        log.Info("[" + id + "] Iniciando la lectura del Request.");
        requestBuffer = new byte[BUFFER_SIZE];
        NetworkStream nst = socket.GetStream();
        nst.BeginRead(requestBuffer, 0,BUFFER_SIZE, this.requestReceived, nst);
    }catch(Exception ex)
    {
        log.Error("[" + id + "] Se presento un problema al iniciar la lectura del Request: " + ex.Message);
        closeSocket();
    }
}


/*Metodo Request.startReceiveAsync*/
public void requestReceived(IAsyncResult ar)
{

    try
    {   
        NetworkStream nst = socket.GetStream();
        nst.EndRead(ar);
        string request = Encoding.UTF8.GetString(requestBuffer, 0, BUFFER_SIZE);
        log.Info("[" + id + "] Request recibido: [" + message +"]");
        RunForIVR(request);
    }
    catch (Exception ex)
    {
        log.Error("[" + id + "] Se presento un problema al recibir el Request: " + ex.Message);
        closeSocket();
    }

}

Dentro del procede de atención se puede requerir el mantener en espera el envío del Response hasta que se tenga una condición predeterminada En este caso se sugiere crear métodos asincrónicos y evitar utilizar los métodos Wait o Sleep, por que la convinación Thread-Socket-Wait/Sleep tiene altas probabilidades de bloquear el socket socket y, por consecuencia, la recepción y escritura de otros sockets.

Para estos casos se debe hacer uso de los métodos asincrónicos (async y await) para evitar detener la ejecución de los threads y evitar bloquear los sockets. Un ejemplo:

Suponga que dentro del método RunForIVR se debe invocar un Webservices por medio de un método estático llamado setSegment, de la clase TrackingRequest, que se encuentra en otra clase. La ejecución del método setSegment no debe interrumpir la ejecución de RunForIVR, pues no es importante el resultado de la operación setSegment para la fabricación del Response, pero se debe considerar que la invocación del Webservice puede retrasar la ejecución por un tema de Timout, por ejemplo.

En este caso se debe realizar lo sisguiente

  1. El método setSegment, de la clase TrackingRequest, debe declararse como asincrónico, usando el prefijo async.
  2. Utilizar el método asincrónico del Webservice en conjunto con la palabra await.
  3. El uso de await, del cliente de Webservice, junto con la declaración del método asincrónico seetSegment provocará que la secuencia del programa se regrese un nivel superior al CallStack, RunForIVR, para que continue su ejecución.
  4. Una vez que el Webservice haya terminado, el método setSegment continua su ejecución.
  5. Mientras se esta ejecutando el Webservice, el método RunForIVR invoca el método SendResponse.
  6. SendReposne invoca el BeginWrite, el cual invocará el método closeSocket.
  7. Sobre el método closeSocket se debe invocar el método EndWrite, el cual bloqueará el socket hasta que la transmisión de datos haya terminado.
  8. Terminada la transmisión de datos se cierra el socket, mientras que a la par se esta ejecutando el método setSegment, o ya termino de ejecutarse.

El código es el siguiente:

Clase TrackingRequest:

public static async Task setSegment(TrackingBean bean)
{
   System.Console.WriteLine("Inicia Async");
    try
    {
        BBVATrackingSoapClient ws = new BBVATrackingSoapClient("BBVATrackingSoap", url);
        ws.InnerChannel.OperationTimeout = TimeSpan.FromSeconds(1.5); // Se da un segundo de toleracncia para llevar a cabo la execucion del

        AddSegmentWS1 result = null;
        
        //Al momento de invocar el método AddSegmentAsync, se regresa el control al metodo superiro que invoco al metodo setSegment
        result = await ws.AddSegmentAsync(bean._key, bean._entity, bean._service, ACTION.OPEN_SEGMENT,CLOSE_DESC.NONE);

        if(result.AddSegmentWSResult.ERR_CODE == 0)
        {
            if (CFGManager.GetCFGManager().DebugActive)
                log.Debug("[" + bean._id + "] Operacion de segmento realizada con exito.");
        }       
        else
            log.Error("[" + bean._id + "] No se pudo registar la operacion del segmento, se obtuvo la siguiente respuesta: " + result.AddSegmentWSResult.ERR_DESC);

        result = null;
        ws = null;
    }
    catch(Exception ex)
    {
        log.Error("[" + bean._id + "] No se pudo registar la operacion del segmento. Error: " + ex.Message );
    }

    bean = null;
    
    System.Console.WriteLine("Termina Async");
    return 0;
}


El código restante para atender el Request e invocar el Response:

public void RunForIVR(string Request)
{
    string Response
 //Procesa el Request
 
 TrackingBean bean;
 //...
 TrackingRequest.setSegment(bean); //Este metodo es asincronico, por lo que no esperara a que finalice.

 //...
 //Obtiene el Response
 
 Response = "Informacion que sera enviada como respuesta";


 //Envia la respuesta
 SendResponse(Response);
}



public void SendResponse(string Response)
{
    StringBuilder sb = new StringBuilder();
    sb.Append(Response);
    sb.Append('\0', BUFFER_SIZE - Response.Length);
    string message = sb.ToString();

    log.Info("[" + id + "] ivrTrans CMD: [" + idCMD + "] RESPONSE: [" + Response + "]");

    NetworkStream nst = socket.GetStream();
    byte[] buffer = new byte[BUFFER_SIZE];
    for (int i = 0; i < BUFFER_SIZE; i++)
        buffer[i] = (byte)message.ElementAt(i);

    nst.BeginWrite(buffer, 0, BUFFER_SIZE, this.closeSocket, nst);// (new AsyncCallback(this.closeSocket));
}

public void closeSocket(IAsyncResult ar = null)
{
    
    try
    {
        if (ar != null) //Since 4.24
        {
            NetworkStream nst = socket.GetStream();
            nst.EndWrite(ar);
        }

        socket.Close();
        socket = null;
    }catch(Exception ex)
    {
        log.Warn("[" + id + "] Se presento un problema al cerrar el socket. Error: " + ex.Message + Environment.NewLine + ex.StackTrace);
    }
    log.Info("[" + id + "] Socket cerrado.");
}

lunes, 27 de marzo de 2017

Uso de la librearia Thread en C++11

Este post contiene el enlace hacia el repositorio de GitHub que contiene un código de ejemplo para maneo de Thread por medio de los medios que proporciona C++11.

Las características de este ejemplo son:

  • Implementación de los Threads de C++11 sobre una clase de C++.
  • Crear dos thread que este utilizando diferentes métodos de la misma instancia.
  • Uso de la librería use of unique_lock  en conjunto con la librería condition_variable.
  • Uso de Mutex para la sincronización de la zonas compartidas.
  • Uso de la librería condition_variable para suspender y  reactivar cada uno de los threads creados en el ejemplo, de manera manual.
  • Uso de la librería condition_variable para suspender el thread y reactivarlo después de un tiempo predefinido.

El enlace al repositorio y su documentación, en ingles, es:


Librería JSON para C++

Este post contiene el link hacia una librería para interpretar documentos estructurados bajo el formato JSON (Standar ECMAC-404). Las características de esta librería son:


  1. Trabaja con formato de cadena UTF-8.
  2. No requiere librerías adicionales.
  3. Modificar el documento JSON
  4. Permite crear documentos JSON.


El link hacia el repositorio GitHub es, la documentación esta en ingles:

https://github.com/jorgemedra/JSONLib

Diagramas de UML y BPMN.

A los colegas que estudiaron Ingeniería o Licenciatura en Sistemas, recordarán la materia de Fundamento de Ingeniería de Software, materia que tome en mi caso (Ingeniero). Sobre esta materia se plantea de temas como ciclos de vida del software, métricas de medición, técnicas de modelado, herramientas case y un largo etcétera.  Entre toda la información que recibimos en dicha materia recordarán los diagramas de flujo para modelar un algoritmo (Conjunto ordenado de instrucciones sistemáticas que hallar la solución de un tipo de problemas), de los cuales son muy utilizados hasta la fecha, pero desde mi perspectiva son insuficientes, tema del cual quiero compartir mi opinión.
Durante los 14 años que he estado trabajando diseñando y creando software para resolver temas de integración en diferentes productos, he llegado a utilizar estos diagramas (Diagramas de Flujo) y siempre me había quedado con la sensación de que requería algo más para detallar excepciones, eventos asíncronos, interrupciones, y todo esa clase de situaciones que los desarrolladores de hoy en día nos encontramos. Fue entonces cuando me acerque al estándar UML (Unified Modeling Language ), un estándar diseñado por la Object Managment Group (OMG).
UML no solo adapto a sus estándar la metodología de modelar un algoritmo de los Diagramas de Flujo que nos enseñaron en los años 80’s y 90’s, sino que también extendió su capacidad de modelado a un entorno de Objetos, llegando a obtener diversos tipo de diagrama de propósito especifico, agrupados en dos principales grupo: Estáticos y Dinámicos.
Es con los diagramas Estáticos que UML nos llevo a adentrarnos en las necesidades del cliente con la  implementación de los diagramas de Casos de Uso, que no son más que un esfuerzo por hacerle ver al desarrollador que parte del negocio  se requiere atender con la implementación con el sistema de información que se esta diseñando. Mientras que los diagramas de clases nos permiten ubicar las diferentes entidades que interactuarán en el nuevo sistema de información que se esta modelando.
Por otro lado, los diagramas Dinámicos nos permiten apreciar la interacción con las diversas entidades que se encuentran en el sistema de información, interacción que se lleva a cabo a través del tiempo, es por eso que se conocen como diagramas Dinámicos. Este tipo de característica nos permite ver si nuestro diseño presenta el comportamiento deseado, tomando como referencia el problema que queremos solucionar.
Pero a medida que aumentaba mi interacción con el cliente y su usuario final, empecé a tener las mismas dudas que me surgieron con los Diagramas de Flujo, específicamente al entender el entorno de negocio en el cual se quiere implementar el sistema informático.

Si bien, UML contiene la sección de Casos de Uso, que es un diagrama que describe como interactuarán los diferentes actores con el nuevo sistema de información, es una vista estática y que describe una caso bajo una narrativa, y es ahí donde radica su punto débil. El ser humano es una persona que puede entender de mejor manera una situación o un mensaje si este se representa de manera gráfica, es por eso que los modelos son de gran ayuda. Es por esta razón  que me dedique a la tarea de buscar como modelar un caso de uso bajo un modelo dinámico y gráfico, características que permitirían al desarrollador entender más a fondo y de manera más rápida el entorno del negocio y su problemática a solucionar. El resultado de mi búsqueda termino en los diagramas BPMN ( Business Process Model & Notation), estándar que también ha sido establecido por la OMG.

BPMN fue creado para modelar procesos de negocio, en los cuales se encuentras diversas interacciones. Su principal característica es la de representar un proceso de negocio  de manera gráfica evitando crear documentos de narrativas largas y complejas, como se viene realizando en los Casos de Uso (UML) o en los diagramas de Cross Funtional. También es una realidad que ha estado creciendo el número de empresas que han adoptado este estándar para modelar sus procesos internos.
Este tipo de modelado (BPMN) permite a las personas el conocer el negocio y su funcionamiento sin tener que leer un documento, pues solo deben conocer las reglas básicas del BPMN para la interpretación del modelo. Cabe mencionar que aprender a leer un diagrama BPMN es una actividad que no llevaría más de una hora y de la cual no se requiere se experto en BPMN, basta con tener a la mano una hoja de referencia de los símbolos utilizados en BPMN.
Así pues concluyo que, con la ayuda de BPMN es posible tomar en cuenta la etapa que varios desarrolladores no se interesan por considerar, el Análisis del Negocio. Con un análisis del negocio claro y representado en un modelo de fácil lectura, se puede integrar a los desarrolladores en una dinámica en donde todos los involucrados de un proyecto hable el mismo idioma, pues se tiene claro el entorno de negocio y su problemática a solucionar. Mientras que los modelos de UML es la metodología ideal para llevar a cabo el diseño del sistema de información (La solución).