Introducción
Bienvenidos a otro artículo en «Hola Windev». Hoy vamos a abordar un tema crucial para cualquier empresa: cómo implementar un sistema de respaldos en tiempo real para bases de datos HFSQL.
Desarrollo
Servidor WebSocket
Para la configuración y el desarrollo del servidor de WebSockets debemos tener claro como funciona este tipo de servicios.
Por defecto vamos a tener algunos eventos disponibles que tenemos que tener en cuenta. Estos son:
- Evento “Bucle”
- Este evento se ejecuta eternamente, es un loop infinito. Todo lo que este dentro de este evento se va a ejecutar una y otra vez sin parar.
- Evento “Nueva conexión”
- Este evento se ejecuta cuando se realiza una nueva conexión por parte de un cliente.
- Evento “Mensaje recibido”
- Este evento se ejecuta cuando se recibe un mensaje.
- Evento “Desconexión de un cliente”
- Este evento se ejecuta cuando un cliente se desconecta
- Evento “Finalización del servicio”
- Este evento se ejecuta cuando se finaliza el servicio.
Vamos paso a paso viendo que es lo que ejecutamos en cada evento.
En el Evento “Nueva conexión” llamamos al procedimiento “onConnection”. Es un procedimiento muy sencillo que crea y asigna información para el cliente que se acaba de conectar
PROCEDURE onConnection( Client is websocketClient )
// Generamos un ID único al cliente
jsClient is JSON
jsClient.id = GetUUID()
// Guardamos en el campo "Note" del tipo websocketClient los datos del mismo
Client.Note = jsClient
jsNotification is JSON
jsNotification.eventType = "socket_connected"
jsNotification.socket_data = Client.Note
// Enviamos el mensaje con el estado de la conexión
Client.Send(jsNotification)
En el Evento “Recepción de un mensaje” llamamos a los procedimientos “onReceivedMessage” y “sendAll”.
PROCEDURE onReceivedMessage( Client is websocketClient, jsMessage is JSON )
// Leemos la información del cliente
jsClient is JSON
jsClient = Client.Note
SWITCH jsMessage.eventType
CASE "disconnect"
jsClient.socket_data = jsMessage.socket_data
Client.Note = jsClient
jsNotification is JSON
jsNotification.eventType = "socket_disconnect"
jsNotification.socket_data = Client.Note
//Registramos al cliente
CASE "register"
jsClient.socket_data = jsMessage.socket_data
Client.Note = jsClient
jsNotification is JSON
jsNotification.eventType = "socket_added"
jsNotification.socket_data = Client.Note
//Lo enviamos a todos los clientes conectados... por ahora para hacer un debug
sendAll(jsNotification)
//keep alive
CASE "ping"
jsPong is JSON
jsPong.eventType = "pong"
Client.Send(jsPong)
END
PROCEDURE sendAll( jsMessage is JSON )
//Recorremos el array de clientes conectados y a cada uno de ellos
//le enviamos el mensaje
Client is websocketClient
FOR EACH Client OF WebSocketListConnectedClient()
Client.Send(jsMessage)
END
En los eventos “Desconexión” desconectamos al cliente y en “Finalización del servicio” enviamos un mensaje usando el procedimiento sendAll.
Ahora, hay un agregado en esta configuración del websocket que estamos configurando. Vamos a crear un proceso de supervisión para la tabla que va a ser afectada por el Webhook. Para esto usamos la función HTrack
Y lo haremos de esta manera:
HTrack(respaldos,newWebhookEvent,hRecNumAll,hsAdd)
Se podría crear un evento directo en el Webhook también, a efectos de mostrar el funcionamiento de HTrack es que vamos a utilizar esta alternativa. Si se desea, en lugar de HTrack, se podría enviar el mensaje directamente desde la API Rest al momento de recibir el POST.
El deploy de nuestro servidor de WebSockets se hace como cualquier otra aplicación que subimos a un WAS. Una vez hecho esto podríamos testear con Postman que este todo ok de esta manera:
Como vimos anteriormente, al conectarnos se ejecuta el procedimiento onConnection y se envía un mensaje con el estado de la conexión que remarco en la imagen.
Con esto último ya tenemos nuestro servidor de WebSockets funcionando y testeado.
Notificación vía Webhook
Antes de comenzar a programar el WebHook vamos a ver la ayuda de HFSQL respecto al uso de WebHooks por parte del servidor de base de datos.
La ayuda nos muestra los datos que vamos a recibir al momento de finalizar una tarea de respaldo.
Teniendo esto en claro vamos a generar un archivo de datos en nuestro análisis:
A los campos que nos va a devolver el HFSQL le sumo un campo rawContent en el cual guardaremos el JSON tal cual lo recibimos, el serverIP para guardar la IP del servidor y un campo timeStamp para tener el dato de cuando fue creado el registro.
Con esto ya comenzamos a programar el Webhook (o gancho web según la ayuda de PCSoft 😉 )
Para programar el Webhook podemos hacerlo de la manera sencilla o de la complicada, como estoy corto de tiempo prefiero la sencilla …
Para esto tenemos que ubicar la descripción de nuestro webservice, hacer click derecho y agregar nuevo punto de entrada (Endpoint).
Esto nos abre una ventana en la cual tenemos que elegir Puntos de entrada para acceder a un archivo de datos, seleccionamos el archivo que acabamos de crear y avanzamos hasta que nos solicite los puntos de entrada. En este caso yo voy a dejar solamente los GET y el POST. No me interesa, de momento, el DEL ni el PUT.
Nos quedaría algo así
PCSoft crea automáticamente la clase, si esto usa POO a full y los métodos necesarios para lograr que funcione todo a la perfección. Estos métodos son 100% modificables, así que si los queremos cambiar podemos, de hecho es lo que vamos a hacer a continuación.
Si miramos el metodo “Creation” vamos a ver esto:
El primer bloque nos muestra un metodo de autenticación, a efectos de no desvirtuar mucho la idea de este post vamos a dejarlo por defecto (viene vacío por lo cual no valida nada). Si quieren se puede hacer la validación que gusten, quizás les interese el artículo que hay en Windev Uruguay al respecto:
Webservices Rest – Autenticación – Windev Uruguay
En el segundo bloque de código tenemos la creación del registro. Ellos, (PCSoft), realizan unos pasajes entre metodos pero en resumen lo que hacen es un reset del archivo “respaldos” y luego una deserialización del JSON que estamos recibiendo en el POST.
La modificación que sugiero en este caso es la siguiente:
Si recuerdan bien tenemos dos campos mas para agregar aparte de los que nos envía el HFSQL en cada tarea de respaldos. De este modo es que los asignamos a cada campo de nuestro archivo de datos.
Con esto terminamos la programación de nuestro Webhook. Podemos testearlo en Postman y ver como reacciona la conexión que tenemos abierta a nuestro servidor de websockets:
Ya tenemos todo listo, ahora vamos a programar el cliente Windows desde el cual inicializaremos la tarea de respaldo desde una ubicación remota y recibiremos las alertas de nuestro servicio.
Aplicación Windows para Respaldos
Ahora si, vamos a programar nuestra aplicación cliente para gestionar los respaldos.
Esta es la ventana, ya se es fea, vamos que estamos practicando!
Como ven en esta ventana tenemos dos botones y una tabla.
La tabla se llena con una query que trae todos los registros de la base de datos. Y los botones uno se usa para enviar mensajes por sockets para poder testear que este todo ok y el otro genera un respaldo.
Vamos por partes. En el código de inicio de la ventana tenemos que programar algunas cosas para comenzar.
PROCEDURE MyWindow()
gs_SessionID is string = "ID_UNICO_PARA_EL_CLIENTE"
gs_Server is string = "192.168.200.164"
IF SocketExist(gs_SessionID) = False THEN
WebSocketClientConnect(gs_SessionID,SocketConnect_Callback,gs_Server,80,"/websockets_testing")
END
INTERNAL PROCEDURE SocketConnect_Callback( skt_Event, skt_Message )
json_Mensaje is JSON = skt_Message
SWITCH skt_Event
CASE SocketOpening
//Evento keepalive
ThreadExecute("Timer",threadSecure,Timer_Callback)
INTERNAL PROCEDURE Timer_Callback()
jsPing is JSON
jsPing.event = "ping"
SocketWrite(gs_SessionID, jsPing)
ThreadPause(5 s)
END
//Al recibir un mensaje del websocket evaluamos el event al callback
CASE SocketMessage
SWITCH json_Mensaje.eventType
//
// Inicia bloque para manejo de los eventos socket
//
CASE "backup"
RequestRefreshUI()
CASE "socket_connected"
js_Register is JSON
js_Register.event = "register"
js_Register.socket_data = gs_SessionID
SocketWrite(gs_SessionID,js_Register)
CASE "pong"
OTHER CASE
Trace("Evento Socket no controlado: " + json_Mensaje.eventType)
Trace(json_Mensaje.data)
END
//Error
CASE SocketError
Trace("Ha ocurrido un error: ([%skt_Message%])")
//Cierre del servidor
CASE SocketClosing
Trace("La conexión con el servidor de Sockets ha sido interrumpida: ([%skt_Message%])")
OTHER CASE
Trace("Error sin especificar: ([%skt_Message%])")
END
END
Lo que tenemos aquí es el proceso de conexión al websocket y usamos un callback para capturar las respuestas de forma asincrona.
Basicamente lo que tenemos es el manejo de los eventos que vamos a estar recibiendo desde el servidor de websockets. Lo novedoso es que, si se fijan, tenemos programado un evento keepalive (un ping) para mantener la conexión activa.
Luego tenemos un evento “backup” que lo que va a hacer es refrescar la tabla de forma asíncrona usando el proceso RequestRefreshUI(), esta es una función que yo utilizo mucho (muchísimo en realidad). Al llamar a esta función nuestro sistema nos va a dirigir a un evento de la ventana, “Solicitud para actualizar la visualización”.
De este modo cuando se reciba un evento “backup” desde el websocket se va a actualizar la tabla de forma automática.
Vamos a ver los botones ahora.
En el botón “Enviar mensaje” tenemos este código muy sencillo:
Simplemente envía un mensaje por socket a nuestro servidor.
Y ahora si, la parte final. El botón “Backup”.
En este vamos a ver como se realiza un respaldo de forma remota de un servidor HFSQL.
Voy a mostrarles el código y vamos a analizarlo juntos:
En la primer sección tenemos la declaración de la tarea de respaldo. El código de esta parte del procedimiento es bastante claro. Se crea una descripción del bakcup y se ejecuta. Dentro de esta definición tenemos la propiedad WebhookAfter, ahi es donde ingresamos la URL de nuestro Webhook, el que programamos en el paso anterior.
El segundo bloque revisa que el respaldo haya terminado, lo hice de manera sencilla y se puede (mejor dicho debe) mejorar, entre otras cosas meterlo dentro de un thread para que no bloquee la aplicación.
Y el tercer bloque lo que hace es descargar el ZIP con el respaldo.
No tiene mucho misterio vieron?
Ahora vamos a verlo en acción.
Conclusión
Espero que este artículo te sea de utilidad y te ayude a entender cómo puedes implementar un sistema robusto de respaldos en tiempo real utilizando exclusivamente tecnologías de PCSoft.
Invitación
Si este artículo te ha resultado útil, no olvides unirte a nuestro servidor de Discord «Hola Windev» para interactuar con otros apasionados del desarrollo en PCSoft.