Analizar las limitaciones de una aplicación web basada en sondeo

Completado

Polling-based web application.

La arquitectura actual de la aplicación notifica información sobre acciones mediante la recuperación de cambios desde el servidor en función de un temporizador. Este diseño se suele denominar diseño basado en sondeo.

Antes de analizar las limitaciones, vamos a revisar la arquitectura actual. El servidor es responsable de almacenar la información sobre las acciones y el cliente presenta los datos en el explorador.

En la siguiente unidad vamos a configurar la solución actual en el equipo local.

Servidor

La información sobre el precio de las acciones se almacena en el servidor en una base de datos de Azure Cosmos DB. Cuando se desencadena mediante una solicitud HTTP, la función usa enlaces para devolver el contenido de la base de datos.

La función denominada getStocks es responsable de leer la información sobre acciones de la base de datos. Como se ha mencionado, la conexión a la base de datos de Azure Cosmos DB se logra mediante un enlace de entrada. Este enlace se configura en el archivo function.json, como se muestra en el siguiente fragmento de código.

{
  "bindings": [
    {
      "type": "httpTrigger",
      "authLevel": "anonymous",
      "direction": "in",
      "name": "req",
      "methods": ["get"]
    },
    {
      "type": "http",
      "direction": "out",
      "name": "res"
    },
    {
      "type": "cosmosDB",
      "direction": "in",
      "name": "stocks",
      "ConnectionStringSetting": "AzureCosmosDBConnectionString",
      "databaseName": "stocksdb",
      "collectionName": "stocks"
    }
  ]
}

El primer enlace (httpTrigger) de la matriz define cómo se desencadena la función.

La configuración... mediante la propiedad:
define la función como una función desencadenada por HTTP type
permite solicitudes entrantes no autenticadas authLevel, direction
expone el contexto de solicitud a través de un parámetro denominado req name
acepta solicitudes GET methods

El segundo enlace (http) define lo que se devuelve desde la función.

La configuración... mediante la propiedad:
permite a la función devolver una respuesta HTTP type, direction
expone el contexto de respuesta a través de un parámetro denominado res name

El tercer enlace (cosmosDB) establece una conexión a Azure Cosmos DB.

La configuración... mediante la propiedad:
convierte en disponibles datos de Azure Cosmos DB cuando se llama a la función type, direction
expone los datos a la función a través de un parámetro denominado stocks name
se conecta a los datos de Azure Cosmos DB con una cadena de conexión ConnectionStringSetting
apunta a la base de datos stocksdb databaseName
apunta a la colección de datos stocks collectionName

Con estos enlaces, las solicitudes GET a getStocks convierten los datos en disponibles a través del parámetro stocks. Como puede ver en el siguiente fragmento de código, el código de función para recuperar información sobre acciones es sencillo gracias a la eficacia de los enlaces de Azure Functions.

module.exports = async function (context, req, stocks) {
    context.res.body = stocks;
};

Cliente

El cliente de ejemplo usa Vue.js para crear la interfaz de usuario y el cliente HTTP Axios para controlar las solicitudes a la función.

La página usa un temporizador para enviar una solicitud al servidor cada cinco segundos a fin de solicitar acciones. La respuesta devuelve una matriz de acciones, que, luego, se muestran al usuario.

const LOCAL_BASE_URL = 'http://localhost:7071';

const app = new Vue({
    el: '#app',
    interval: null,
    data() {
        return {
            stocks: []
        }
    },
    methods: {
        async update() {
            try {
                const apiUrl = `${LOCAL_BASE_URL}/api/getStocks`;
                const response = await axios.get(apiUrl);
                app.stocks = response.data;
            } catch (ex) {
                console.error(ex);
            }
        },
        startPoll() {
            this.interval = setInterval(this.update, 5000);
        }
    },
    created() {
        this.update();
        this.startPoll();
    }
});

Una vez que el método startPoll comienza el sondeo, se llama al método update cada cinco segundos. Dentro del método update, se envía una solicitud GET a la función getStocks y el resultado se establece en app.stocks, lo que actualiza la interfaz de usuario.

El código de cliente y servidor es relativamente sencillo, pero, como se descubre en nuestro análisis, esta simplicidad aporta algunas limitaciones.

Compatibilidad de CORS

En el archivo local.settings.json, la sección Host incluye los siguientes valores.

  "Host" : {
    "LocalHttpPort": 7071,
    "CORS": "http://localhost:8080",
    "CORSCredentials": true
  }

Esta configuración permite que una aplicación web que se ejecuta en localhost:8080 realice solicitudes a la aplicación de función que se ejecuta en localhost:7071. La propiedad CORSCredentials indica a la aplicación de función que acepte las cookies de credencial de la solicitud.

CORS es una característica de HTTP que permite que una aplicación web que se ejecuta en un dominio tenga acceso a recursos de otro dominio. Los exploradores web implementan una restricción de seguridad denominada directiva del mismo origen que impide que una página web llame a las API de otro dominio diferente; CORS proporciona una forma segura de permitir que un dominio (el dominio de origen) llame a las API de otro dominio.

Puede establecer reglas CORS individualmente para cada uno de los servicios de Azure Storage. Para ello, llame a Set Blob Service Properties, Set File Service Properties, Set Queue Service Properties y Set Table Service Properties. Una vez establecidas las reglas de CORS para el servicio, una solicitud debidamente autorizada realizada contra el servicio desde un dominio diferente se permite o no según las reglas que haya especificado.

Análisis de la solución actual

Vamos a pensar en algunos de los inconvenientes de este enfoque de sondeo basado en temporizador.

En el prototipo de sondeo basado en temporizador, la aplicación cliente se pone en contacto con el servidor tanto si hay cambios en los datos subyacentes como si no. Cuando se devuelven datos desde el servidor, toda la lista de acciones se actualiza en la página web, una vez más, independientemente de los cambios en los datos. Este mecanismo de sondeo es una solución ineficaz.

La selección del mejor intervalo de sondeo para el escenario también supone un reto. El sondeo obliga a elegir entre el costo de cada llamada al back-end y la rapidez con la que se quiere que la aplicación responda a los nuevos datos. Los retrasos a menudo existen entre el momento en que los nuevos datos están disponibles y el momento en que la aplicación los detecta. En la ilustración siguiente se muestra el problema.

An illustration showing a timeline and a polling trigger checking for new data every five minutes. New data becomes available after seven minutes. The app isn't aware of the new data until the next poll, which occurs at 10 minutes.

En el peor de los casos, el retraso potencial para detectar datos nuevos es igual que el intervalo de sondeo. Así que, ¿por qué no usar un intervalo menor?

A medida que la aplicación crece, la cantidad de datos intercambiada entre el cliente y el servidor se convierte en un problema. Cada encabezado de solicitud HTTP incluye cientos de bytes de datos junto con la cookie de la sesión. Toda esta sobrecarga, especialmente si hay una carga elevada, crea recursos desperdiciados y afecta innecesariamente al servidor.

Ahora que está más familiarizado con el punto inicial de la aplicación, es hora de hacer que la aplicación se ejecute en el equipo.