He decidido explorar el uso de la tecnología WebSockets para aplicaciones que requieren el envío de mensajes iniciados desde el servidor a clientes web mediante conexiones de persistentes de dos vías.

Estuve explorando diversas opciones. Es interesante que Apache y los sistemas de hospedaje compartido clásicos (tipo CPanel) no incluyan opciones para el uso de esta tecnología.

Explorando encontré que recientemente Amazon agregó WebSockets a su oferta de servicios en la nube. Me pareció particularmente sencillo de aprender, así que acá les comparto un proyecto inicial.

En Resumen

Este proyecto consiste en implementar un sistema de votación tipo poker-planning de forma que el resultado de la votación sea distribuido a todos los participantes en el momento en que todos hayan terminado de votar.

Para esto cada participante debe iniciar una conexión bidireccional con un servidor de WebSockets en un tópico común (partida). Uno de los participantes (dealer) enviará un mensaje de inicio de votación que todos los demás participantes recibirán. Cada uno responderá a dicho mensaje con su voto. Una vez que todos los participantes hayan votado, el sistema enviará un único mensaje a todos los participantes con el resultado de la votación.

El siguiente diagrama ilustra la diferencia entre el modelo convencional HTTP «solicitud/respuesta» y el modelo bi-direccional de WebSocket. Mientras que en el modelo HTTP solamente el cliente inicia enviando mensajes de solicitud al servidor y este responde, en el modelo WebSocket, el cliente realiza una solicitud inicial para una conexión bi-direccional, a la que el servidor responde y a partir de ahí ya sea el cliente o el servidor pueden tomar iniciativa y enviar mensajes a su contraparte.

HTTP:
CLIENT =========== REQUEST ==========>  SERVER
       <========== RESPONSE ==========
WS:
CLIENT ======== REQUEST UPGRADE ======> SERVER
       <======= UPGRADE RESPONSE ====== 
       <========== MESSAGES ==========>
       <========== MESSAGES ==========>
       <========== MESSAGES ==========>

Los servicios principales a utilizar de parte de AWS son:

  1. API Gateway (Modo WebSockets)
  2. Funciones Lambda
  3. DynamoDB

Nuestro itinerario será:

  1. Crear Lambda’s base (connect, disconnect, default)
  2. Crear un Rol para la ejecución de Lambda’s
  3. Crear un API Gateway de tipo WebSocket
  4. Utilizaremos wscat para interactuar con nuestro WebSocket API
  5. Crear las rutas especializadas para el juego de poker planning
    1. Crear una partida
    2. Unirse a una partida
    3. Crear Votación
    4. Votar
    5. Cerrar votación

Lambda’s Base y Rol de Ejecución

Nuestro servidor WebSockets es en realidad una implementación del servicio API Gateway de AWS que utiliza el protocolo para WebSocket.

Cada implementación de dicho protocolo en AWS presenta tres rutas opcionales predeterminadas (connect, disconnect, default) las cuales estarán «alambradas» a una función lambda de AWS cada una que ejecutará la lógica que queramos. Estas lambdas serán accesibles para el API Gateway mediante un rol de acceso que definimos a continuación.

Creando un Rol

Antes que nada vamos a crear un Rol (Role) que defina el acceso a la ejecución de funciones LAMBDA. Este rol lo usaremos posteriormente para dar acceso a la ejecución de lambdas de nuestro sistema.

Para esto vamos a acceder al servicio de Gestión de Acceso e Identidades (IAM), y ahí vamos a «Roles». Se puede acceder fácilmente a este servicio buscando la palabra «IAM» en la casilla de búsqueda.

Identity and Access Management > Roles

En la página de gestión de roles, hacemos clic en el botón [Create Role] donde elegimos el tipo «AWS Service» y el servicio «Lambda» para luego dar clic en [Next: Permissions]

Creación de Rol – Paso 1 «Tipo de Rol y Caso de Uso»

En la página del paso 2, adjuntar permisos, usamos la casilla de búsqueda para ubicar el permiso «AWSLambdaExecute», seleccionamos la casilla correspondiente.

Creación de Rol – Paso 2 «Agregar Permisos»

El tercer paso permite de forma opcional agregar etiquetas (tags) con llave/valor para describir de forma más precisa el rol y poder administrarlo de mejor manera en un escenario donde haya muchos roles. Para nuestro caso podemos dejar el rol sin etiquetas y hacer clic en [Next: Review] para pasar al cuarto paso.

En este cuarto paso debemos darle un nombre a nuestro rol. Yo ne nombré «pokerface_role» y hacemos clic en [Create Role]

Creación de Rol – Pasop 4 «Nombre y Revisión Final»

De vuelta en la página de gestión de roles, nuestro nuevo rol aparece en la lista.

Página de Gestión de Roles

Creando las Funciones Lambda predeterminadas

En AWS, el término Funciones Lambda se refiere al servicio que consiste en definir pequeños pedazos de código con un ambiente común que podemos ejecutar de diferentes formas. En nuestro caso las ejecutaremos para cada solicitud a nuestro servicio API Gateway.

Ahora vamos a crear las lambdas predeterminadas que usaremos luego en nuestro servicio API Gateway para las diferentes rutas predeterminadas de WebSockets:

RutaFunción Lambda
$connectpokerface_lfn_connect
$disconnectpokerface_lfn_disconnect
$defaultpokerface_lfn_default
Funciones Lambda Predeterminadas para cada ruta del API

En la consola de administración de AWS, buscamos el servicio «Lambda» y hacemos clic en el botón [Create function]

Seguimos la creación «Author from Scratch» y le damos nombre a nuestro nuevo LAMBDA (ejemplo: «pokerface_lfn_connect»)

Escojemos el tipo de ambiente «Runtime» que puede ser Node.js 12.x (o también Python 3.8.

Hacemos clic en la flecha junto a «Change default execution role» para seleccionar «Use an existing role» donde elegimos nuestro rol «pokerface_role»

Finalmente hacemos clic en [Create Function] abajo para crear un nuevo Lambda.

Creación de una nueva función Lambda

Dejaremos estas funciones con el código predeterminado. Ejemplo en NodeJS:

exports.handler = async (event) => {
    // TODO implement
    const response = {
        statusCode: 200,
        body: JSON.stringify('Hello from Lambda!'),
    };
    return response;
};

Repetimos este paso para cada una de las 3 funciones que definimos en nuestra tabla (pokerface_lfn_connect, pokerface_lfn_disconnect, pokerface_lfn_default)

Una vez creadas nuestro rol y nuestras funciones Lambda, podremos proceder a la creación de nuestro API Gateway de WebSocket.

Creación de nuestro API Gateway

El servicio API Gateway de AWS permite la implementación de rutas (URL’s) por medio de las cuales un sistema en la nube puede hacer llamados o conexiones a otros servicios definidos en AWS. Por ejemplo, podemos usar la url «https://mi-api-gateway-service.aws.com» para pasar datos a una función LAMBDA que realizará alguna operación de base de datos en nuestro sistema.

Recordemos que el término «API» representa una Interfaz de Programación de Aplicaciones (Application Programming Interface). Es decir definimos una interface para acceder a la programación de nuestra aplicación.

Las instancias API Gateway pueden ser de diversos tipos, por ejemplo HTTP API, WebSocket API, REST API o REST API Privada.

Creando una API Gateway de tipo Websocket

Lo primero que haremos es buscar el servicio API Gateway en nuestra consola de administración de AWS.

Buscando el servicio API Gateway en AWS Console

Hacemos clic en el botón [Create API]

Creando una API en API Gateway de AWS

En el paso para seleccionar un tipo de API, usamos «WebSocket API»

Selección de tipo de API como WebSocket API

Al hacer clic en [Build] se nos presenta un proceso de 5 pasos para crear nuestro API Gateway. Iniciamos dándole un nombre apropiado como por ejemplo «pokerface_wss» en alusión a nuestro sistema pokerface y que se trata del protocolo web sockets secure. En la casilla de la «expresión de selección de ruta» usamos request.body.action para indicar que usaremos el cuerpo de la solicitud y una propiedad «action» para definir diferentes rutas. (luego explicaremos esto con más detalle)

Nos saltaremos el proceso automatizado al hacer clic en [Create blank API]

Creación de un Web Sockets API vacío.

Al hacer clic en [Create blank API] saltamos en el proceso al paso 5 donde revisamos todas las demás etapas (vacías) y hacemos clic en [Create]

Creación de API

Esto nos lleva a la página donde podemos configurar nuestra nueva API

Página de administración del API

Se nos presentan tes rutas predeterminadas ($connect, $disconnect y $default) que nos servirán para alambrar nuestra API a las lambdas que creamos anteriormente.

Conectando las Rutas con las Lambdas

En este punto vamos a conectar nuestras rutas predeterminadas con nuestras lambdas.

En la página de administración de nuestro API, hacemos clic en la ruta $connect, lo cual nos muestra el tipo de integración que esa ruta tendrá con nuestro ecosistema AWS. Seleccionamos el tipo «Lambda function», y buscamos el lambda correspondiente pokerface_lfn_connect como se describe a continuación.

En este acto el sistema nos advierte que estamos dándole permiso a nuestra ruta $connect de ejecutar nuestra lambda pokerface_lfn_connect

Repetimos este paso para las rutas $disconnec y $default pero, en esta última agregamos una «Integración de respuesta» haciendo clic en el botón [Add Integration Response]. Este último paso permitirá emitir una respuesta desde el Lambda hacia nuestro cliente.

Agregando Integration Response

Una vez que tenemos nuestras tres lambdas básicas alambradas a las tres rutas predeterminadas de nuestro API, podemos desplegar ese API para que sea accesible desde la nube.

Para esto hacemos clic en Actions > «Deploy API»

Por ser la primera vez, elegimos un nuevo escenario (New Stage) al que llamaremos «dev» y hacemos clic en el botón [Deploy]

Despliegue del API (WebSocket URL)

Nótese que además de desplegar el API en el ambiente dev también configuramos un «throttling» o límite de acceso bastante bajo (10 mensajes por segundo) para nuestras pruebas. Hacemos clic en [Save Changes] y listo.

Los enlaces descritos arriba nos permitirán acceder al websocket API desde la nube.

WSCAT: Probando 123

WSCAT es un módulo utilitario de NodeJS que habilita una especie de terminal para la interacción con WebSockets. En este tutorial asumimos que ya tienes NodeJS instalado en tu computador (puedes ver acá) y que puedes instalar WSCAT usando el comando npm de la siguiente forma:

npm install wscat -g

Una vez instalado bastará con ejecutar en tu computador el siguiente comando para conectarte a tu API:

wscat -c url_al_api

Donde url_al_api es el URL que obtuviste en el paso de despliegue del API en el campo WebSocket URL.

En mi caso sería así:

wscat -c wss://7dnsj7f3ed.execute-api.us-west-2.amazonaws.com/dev

Esto nos abre un programa interactivo de consola que nos permite escribir mensajes que serán enviados a nuestro API de WebSocket.

WSCAT – Ejemplo

Personalizando la respuesta predeterminada

Vimos en el paso anterior que la respuesta fue «Hello from Lambda» lo cual corresponde a la respuesta del código predeterminado de nuestro LAMBDA.

Vayamos a la lambda correspondiente y cambiemos un poco el código:

Código personalizado para lambda predefinida

Una vez que desplegamos este lambda con el botón [Deploy] al enviar un mensaje desde wscat la respuesta cambia de forma significativa.

Respuesta Personalizada

Generando mensajes desde el servidor

Hasta ahora hemos podido demostrar una conexión solicitud/respuesta desde wscat en nuestro computador a nuestro ecosistema de servicios en la nube en AWS.

Ahora haremos lo que HTTP no puede hacer: enviar mensajes desde el servidor sin que haya una solicitud del cliente de por medio.

Lo primero que haremos es facultar a nuestras lambdas para emitir mensajes a los clientes de WebSocket API’s

Para esto actualizaremos nuestro rol para agregar el permiso de AmazonAPIGatewayInvokeFullAccess

Lambda de Prueba

Para esto crearemos una nueva función Lambda con el siguiente código en python:

import json
import boto3

def lambda_handler(event, context):
    connectionId = "VAMOS A PONER EL CONEXION ID ACÁ"
    URL = "https://ew16tuqah7.execute-api.us-west-2.amazonaws.com/dev"
    client = boto3.client("apigatewaymanagementapi", endpoint_url = URL);
    msg = "HELLO FROM SERVER";
    
    response = client.post_to_connection(ConnectionId=connectionId,Data=json.dumps(msg))
    
    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }

En esta ocasión usamos Python por facilidad con la librería boto3 que permite contactar de forma muy sencilla a nuestros clientes.

Nótese que en la línea 5 vamos a «hard-codear» una ID de conexión que tomaremos de wscat más tarde.

Vamos al servicio de funciones Lambda y hacemos click en [Create Function]

Le damos un nombre a la función «pokerface_lfn_prueba_servidor» y elegimos el ambiente Python 3.8, elegimos nuestro rol de ejecución como «pokerface_role» y hacemos clic en [Create Function]

Colocamos nuestro código y hacemos clic en [Deploy]

Ahora vamos a ejecutar WSCAT y oibtener nuestro ID de conexión:

Colocamos ese ID en nuestra lambda en la línea 5 y hacemos clic en Deploy.

Ahora de vuelta en nuestra lambda podemos ejecutar una prueba usando el botón TEST.

Rutas Especializadas

En la 2da Parte de nuestro tutorial estaremos discutiendo las rutas especializadas que nos permitirán implementar el juego de Planning Poker.

Referencias

 5,389 total views,  1 views today

0Shares
Última modificación: diciembre 15, 2020

Autor

Comentarios

Escribe una respuesta o comentario

Tu dirección de correo electrónico no será publicada.

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.