Optimización del rendimiento en Aplicaciones Web y Api's

Optimización del rendimiento en Aplicaciones Web y Api's

@programación

La experiencia del usuario de una aplicación web es uno de los factores clave que impulsan la satisfacción del consumidor y, por lo tanto, el número de consumidores a la aplicación. Y una de las piezas críticas de esta experiencia de usuario es la velocidad y el rendimiento de la aplicación. Tal experiencia de usuario de la capacidad de respuesta de una simple acción o clic en la aplicación, o el tiempo de carga de la aplicación en sí, a su vez depende de una cantidad de factores como el tiempo de respuesta del servidor ascendente y las API HTTP, el tiempo de ida y vuelta de la red, el tiempo incurrido en seguridad y validaciones, etc. Si bien dedicamos mucho tiempo a optimizar el rendimiento del código de nuestra aplicación y las API, también hay otros factores comunes que pueden ayudar a lograr una optimización significativa del rendimiento sin mucho esfuerzo.

Optimización del tiempo de respuesta de las API HTTP

La aplicación web representada en el cliente web o el navegador web de un usuario tiene que interactuar con una o más API HTTP en el servidor, ya sea para obtener datos o actualizar las llamadas en respuesta a las acciones del usuario. El tiempo de respuesta de dichas API también afecta la capacidad de respuesta de la aplicación y la experiencia del usuario. La optimización de este tiempo de respuesta de API incluye no solo la optimización del tiempo de respuesta del lado del servidor, sino también el tiempo de respuesta total percibido en el cliente o navegador.

Grupo de conexiones de base de datos/API ascendente en el servicio de API

Con numerosas aplicaciones moviéndose hacia el modelo de diseño de microservicios, el servicio que aloja una API puede necesitar realizar una o más llamadas API ascendentes a otros servicios o almacenes de datos para obtener datos o desencadenar acciones. Además del tiempo de ida y vuelta de la red y el tiempo de procesamiento en el servidor ascendente, dicha llamada ascendente también debe establecer una conexión HTTP con el flujo ascendente, incluido SSL Handshaking, para garantizar una conexión segura entre los dos servicios. Para evitar tales protocolos de enlace SSL repetitivos para cada llamada ascendente, se puede utilizar un conjunto de conexiones en el cliente del servicio, manteniendo así un conjunto de conexiones activas que se pueden reutilizar para todas las llamadas al mismo servidor ascendente. Dado que establecer una nueva conexión con el upstream suele ser una operación costosa,

Cualquier conexión activa al servidor ascendente en el grupo de conexiones también tiene un tiempo de espera de mantenimiento activo, y la conexión se interrumpe si se vuelve obsoleta y no se usa durante más tiempo que el período de tiempo de espera. Esto puede conducir a un uso ineficaz del conjunto de conexiones para un servicio recientemente implementado con un tráfico de usuarios muy bajo. Una solución alternativa en tales casos es ejecutar un trabajo en segundo plano en el servicio API que puede realizar llamadas de ping ficticias al servidor ascendente cada vez que la conexión está a punto de agotarse y, por lo tanto, mantener la conexión siempre activa.

Emparejamiento de red entre el servicio API y los servidores ascendentes

Una llamada ascendente desde el servicio API a otra API, base de datos o almacén de datos, también incurre en un retraso de ida y vuelta de la red entre el servicio y el ascendente. En los casos en los que es posible configurar la interconexión de redes entre el servidor ascendente y el servicio API, puede ayudar no solo a reducir el retraso de ida y vuelta de la red, sino también a proteger la conexión con el servidor ascendente.

Compresión HTTP

En ciertos casos, la respuesta de API del Servicio de API al cliente, o la respuesta de un servidor ascendente al Servicio de API, puede tener un tamaño grande. Los tamaños de respuesta tan grandes pueden aumentar el retraso de ida y vuelta de la red, debido a la mayor cantidad de bytes transferidos a través de la red. Para superar este problema, HTTP expone encabezados de codificación de contenido para transferir contenido comprimido a través de la red. El servidor que envía una respuesta tan grande puede comprimir la respuesta y usar el encabezado de respuesta HTTP de codificación de contenido para especificar el tipo de compresión utilizada. Además, el cliente debe especificar los tipos de codificación de contenido que puede decodificar para permitir que el servidor use dicha compresión y envíe el contenido comprimido como respuesta. Con el uso correcto de la compresión y, por lo tanto, un menor número de bytes transferidos a través de la red,

Control de caché

El encabezado HTTP de control de caché expone varias directivas de almacenamiento en caché diferentes que pueden ser útiles en varios casos de uso y escenarios. Si bien es posible que no deseemos almacenar en caché una respuesta API en el navegador como almacenamos en caché el contenido estático, algunas otras directivas de control de caché pueden ser útiles en muchos casos en los que es aceptable un ligero retraso o inconsistencia al reflejar los cambios de datos del lado del servidor en el cliente. , al tiempo que mejora significativamente el rendimiento de la aplicación. Como ejemplo, un valor de "obsoleto mientras se revalida" en el encabezado de control de caché permitirá que el cliente acepte una respuesta obsoleta, mientras comprueba de forma asíncrona si hay una respuesta nueva en segundo plano, lo que garantiza una respuesta nueva en un par de recargas de la aplicación o el componente de la aplicación.

Dominio API unificado

Una llamada de API desde el navegador al servicio de API también genera una configuración de conexión y un retraso en el protocolo de enlace SSL. Y si bien podríamos optimizar la latencia de configuración de la conexión a los servidores ascendentes dentro de nuestro servicio de API mediante la agrupación de conexiones, aquí cada usuario utiliza su propia instancia de cliente o navegador y, por lo tanto, una agrupación de conexiones utilizada por el cliente o el navegador es independiente para cada usuario independiente. Por lo tanto, cada primera llamada del cliente a cualquier dominio API tendrá que pasar por dicho tiempo de configuración de la conexión, agregando su latencia al tiempo de respuesta general de la API. Y además, dicha conexión debe actualizarse cada vez que se vuelve obsoleta. Pero la cantidad de configuraciones de conexión de este tipo se puede minimizar mediante el uso de una puerta de enlace API y, por lo tanto, un dominio API unificado para todas las llamadas API del cliente.

Optimización en CORS y Solicitudes Pre-vuelo

El intercambio de recursos de origen cruzado (CORS) permite que un servidor indique que, además del dominio de la página cargada en el navegador, qué otros orígenes (dominios) pueden llamar a las API del servidor o cargar sus recursos desde el navegador. CORS va acompañado de una solicitud de "verificación previa" del navegador al servidor, para verificar si el servidor permitirá la solicitud real. Tales solicitudes de verificación previa pueden aumentar aún más la latencia general al recuperar la respuesta de las API. Pero con el uso correcto de los encabezados de respuesta CORS y la solicitud API, es posible minimizar y evitar solicitudes de verificación previa innecesarias y, por lo tanto, optimizar el tiempo de respuesta general.

En la mayoría de los casos, el Servicio API o Gateway puede establecer el encabezado de respuesta "Access-Control-Max-Age" en su límite máximo de 86400 (24 horas), y así evitar la repetición de tales solicitudes de verificación previa del cliente. Junto con esto, el cliente también puede forzar la solicitud de verificación previa antes de la necesidad real de realizar la llamada a la API y, por lo tanto, ocultar la latencia de la solicitud de verificación previa al usuario. También es posible que el servidor permita llamar a API no confidenciales mediante solicitudes simples y, por lo tanto, evitar por completo las solicitudes de verificación previa para tales llamadas.

Procesamiento por lotes de solicitudes GraphQL o HTTP

Uno de los principales factores que contribuyen al tiempo de respuesta general medido en el cliente es el retraso de ida y vuelta de la red entre el cliente y el Servicio API. También es importante tener en cuenta que este retraso puede variar significativamente según el ancho de banda de la red y la conexión a Internet de los consumidores y, por lo tanto, es crucial monitorear varios percentiles (como 50p/75p/90p/etc) de los tiempos de respuesta generales medidos en el cliente entre los usuarios de la aplicación, para encontrar el rendimiento observado por la mayoría de los usuarios de la aplicación.

Una de las formas de evitar dichos retrasos en la red y optimizar el rendimiento de la aplicación es reducir la cantidad de viajes de ida y vuelta en la red entre el cliente y el servidor. GraphQL es una excelente manera de abordar no solo el problema de la obtención insuficiente que reduce la cantidad de viajes de ida y vuelta de la red, sino que también aborda el problema de la obtención excesiva y, por lo tanto, evita la carga útil de datos innecesaria en respuesta. Otra forma sencilla de reducir los viajes de ida y vuelta de la red es utilizar el procesamiento por lotes de solicitudes HTTP , lo que permite al cliente realizar más de una solicitud API en una sola llamada al servidor.

Aceleradores de API

Si bien GraphQL puede ayudar a reducir la cantidad de viajes de ida y vuelta de la red entre el cliente y el servidor, podemos usar más aceleradores de API para optimizar el retraso del viaje de ida y vuelta de la red desde el cliente hasta el servidor. Azure Frontdoor o Amazon Cloudfront son ejemplos de tales soluciones API Accelerator disponibles en el mercado. Estos proveedores de la nube brindan una conexión de alto ancho de banda de red desde un nodo perimetral cercano al cliente al servidor de aplicaciones, lo que reduce significativamente el retraso de ida y vuelta de la red.

Optimización del contenido estático y la representación de la aplicación

Si bien la sección anterior analiza formas de optimizar el rendimiento de la aplicación web al optimizar el rendimiento de la API de extremo a extremo percibido en el cliente, se pueden usar muchas formas similares para optimizar también el rendimiento del contenido estático y la representación de la aplicación en sí.

Red de entrega de contenido (CDN)

Servir el contenido estático a través de CDN es un enfoque bastante común y popular que se utiliza no solo para minimizar el tiempo de carga del contenido estático en el cliente, sino también para aprovechar la alta disponibilidad proporcionada por la CDN. Content Delivery Network es una red distribuida geográficamente de servidores proxy que trabajan juntos para proporcionar una entrega rápida de contenido de Internet. Muchos proveedores de la nube, incluidos AWS/Azure/Google, brindan dicha solución CDN utilizando servidores CDN distribuidos globalmente ubicados cerca de los usuarios. La distribución de contenido estático más cerca de los visitantes que utilizan dichos servidores CDN ayuda a proporcionar a los visitantes una experiencia fluida con tiempos de carga de página más rápidos.

JS/CSS minificado

Ejecutar una aplicación web en el navegador requiere cargar el código de la aplicación, incluidos los archivos Javascript y CSS, en el navegador. Este código de aplicación puede crecer fácilmente hasta varios megabytes de tamaño, lo que puede ralentizar el tiempo de carga del código de aplicación en el navegador. Aquí es donde JS/CSS Minification viene al rescate y ayuda a reducir el tamaño del código de la aplicación eliminando comentarios, espacios adicionales y procesando nombres de variables. Dicho archivo minificado proporciona la misma funcionalidad al tiempo que reduce el ancho de banda de la red necesario para cargar el código de la aplicación. Si bien la mayoría de los paquetes de módulos JS de uso común, como Webpack, brindan dicha capacidad de minificación lista para usar , también hay varios otros minificadores JS / CSS de código abierto disponibles para ayudar con dicha minificación.

Compresión HTTP

Si bien ya vimos cómo la compresión HTTP puede ayudar a optimizar el rendimiento de nuestras API, también se usa comúnmente para ayudar a reducir los tiempos de carga de los activos estáticos. Muchos servidores web, como NGINX y las CDN de Cloud Provider, brindan soporte inmediato para dicha compresión en activos estáticos, y es fácil aprovechar esta optimización utilizando las configuraciones correctas.

División de código y carga diferida

Si tiene una aplicación grande que consta de varios componentes, puede ser una buena idea dividir la aplicación en más de un paquete, según el caso de uso deseado y la funcionalidad de varios componentes en la aplicación. Como ejemplo, no es necesario cargar el código para un componente de cuadro de chat en el navegador, hasta que el usuario haga clic en un botón de Soporte o Chat. Dado que es una acción específica que impulsa la representación de dicho componente, es posible que no se requiera que el componente se represente para la mayoría de los usuarios de la aplicación. La división del código en varios paquetes puede reducir el tamaño del paquete principal que debe cargarse como parte de la carga de la página y, por lo tanto, reducir el tiempo de carga de la aplicación. Tal retraso en la carga de paquetes basado en la necesidad de renderizar algún componente también se conoce como Lazy Loading, donde la carga de un paquete o componente generalmente se desencadena por alguna acción específica del usuario. Nuevamente, la mayoría de los paquetes de módulos JS comúnmente utilizados admiten tales división de código .

Aunque Code Splitting puede ser muy útil para optimizar el tiempo de carga de la aplicación y la experiencia del usuario, la definición y selección de estos módulos independientes para la carga diferida debe pensarse cuidadosamente para garantizar que no provoque una degradación del rendimiento debido a una división excesiva innecesaria. de código La carga diferida para los componentes de la interfaz de usuario con los que los usuarios interactúan con frecuencia inmediatamente después de la carga de la aplicación, puede generar varias solicitudes de red independientes para cargar todos esos módulos en el momento de la carga de la aplicación y, por lo tanto, varios viajes de ida y vuelta en la red en lugar de uno. Esto puede degradar efectivamente el rendimiento del tiempo de carga de la aplicación en lugar de mejorarlo.

Almacenamiento en caché

El almacenamiento en caché del contenido estático en el navegador puede mejorar significativamente el tiempo de carga de la aplicación para futuras visitas de un usuario. Dado que dicho contenido estático a menudo cambiaría solo en las implementaciones de código nuevo, dicho almacenamiento en caché de contenido puede ayudar a deshacerse de varias solicitudes de red necesarias para cargar el código de la aplicación, para un porcentaje significativo de visitas de usuarios. Y luego, utilizando las estrategias de almacenamiento en caché correctas , siempre puede obligar al navegador a cargar el contenido JS/CSS nuevo cuando cambia el contenido.

Prefetch, Preconnect y Prerender

Si bien los navegadores web modernos utilizan varias técnicas para optimizar el rendimiento de la carga de la página al anticipar lo que probablemente hará el usuario a continuación, el desarrollador de la aplicación web a menudo tendrá la mejor información sobre lo que probablemente hará un usuario después de cargar la página o un conjunto anterior de acciones y, por lo tanto, puede tomar las mejores decisiones sobre qué recursos buscar previamente, o a qué origen conectarse incluso antes de que el usuario active una acción, o qué componente renderizar por adelantado.

Prefetch, Preconnect, Prerender y otras primitivas similares, también conocidas como Sugerencias de recursos , permiten al desarrollador ayudar al agente de usuario o al navegador en el proceso de decisión de a qué orígenes debe conectarse y qué recursos debe obtener y preprocesar para mejorar. rendimiento de la página y, por lo tanto, puede ayudar a ocultar algunas de las latencias de red, procesamiento y representación de los usuarios.

Actualizar a HTTP/2

HTTP/2 ofrece varias mejoras sobre HTTP/1.1, incluidas muchas optimizaciones de rendimiento. Con funciones como multiplexación, compresión de encabezado, transmisión de formato binario, inserción de servidor, HTTP/2 puede ayudar a brindar un rendimiento y una experiencia web mucho mejores, sin siquiera requerir un esfuerzo significativo por parte de los desarrolladores.

Supervisión del rendimiento

Si bien todas las técnicas discutidas en las secciones anteriores pueden ayudar a mejorar el rendimiento de una aplicación, también es importante monitorear las áreas específicas de mejora para una aplicación determinada mediante un monitoreo suficiente de varias acciones del usuario, flujos de trabajo, tiempo de respuesta de API de extremo a extremo, y otros tiempos de carga en la aplicación web. También se requiere un buen marco de recopilación y monitoreo de métricas para analizar las mejoras de rendimiento logradas al incorporar cualquiera de estas técnicas. New Relic RUM o Splunk RUM son solo un par de herramientas entre varias otras herramientas similares que pueden recopilar tales métricas de usuario en la aplicación cliente y, por lo tanto, ayudar a realizar un seguimiento del rendimiento de su aplicación web tal como lo perciben sus usuarios finales.

Referencias


Report Page