La mejor priorización HTTP / 2 para la aceleración web


HTTP / 2 prometió acelerar significativamente la web, y Cloudflare implementó hace mucho tiempo el acceso HTTP / 2 para todos los clientes. Pero una característica de HTTP / 2, la priorización, no cumplió con las expectativas. No porque esté fundamentalmente roto, sino por la implementación en los navegadores.

Hoy, Cloudflare sugiere cambiar la priorización de HTTP / 2, lo que les da a nuestros servidores control sobre las decisiones de priorización que realmente aceleran Internet.

Históricamente, era el navegador el que controlaba cómo y cuándo descargar contenido web. Hoy, para todos los planes pagos, estamos haciendo cambios radicales en este modelo. Transfieren el control directamente al propietario del sitio. En la pestaña "Velocidad" en el panel de Cloudflare, los clientes pueden habilitar la "Priorización HTTP / 2 avanzada": anula la configuración predeterminada del navegador a un esquema de programación mejorado, lo que acelera significativamente el acceso de los visitantes (en algunos casos, vimos un aumento del 50%). Con los trabajadores de Cloudflare, los propietarios de sitios pueden ir más allá y personalizar completamente la configuración para sus necesidades específicas.

Situación actual


Las páginas web consisten en docenas (a veces cientos) de recursos individuales que el navegador descarga y recopila en el contenido final que se muestra. Esto incluye el contenido visible con el que interactúa el usuario (HTML, CSS, imágenes), así como la lógica de la aplicación (JavaScript) para el sitio en sí, publicidad, análisis y balizas de seguimiento de marketing. Desde el punto de vista del usuario, la secuencia en la que se cargan estos recursos es muy importante: esto afecta el momento en que ve el contenido y puede interactuar con la página.

Un navegador es, de hecho, un motor de procesamiento de HTML que pasa a través de un documento HTML y sigue las instrucciones en orden: de principio a fin HTML, construyendo la página a medida que se mueve. Los enlaces de hoja de estilo (CSS) le indican al navegador cómo aplicar estilo al contenido de la página, y el navegador retrasará la visualización del contenido hasta que cargue la hoja de estilo. Las secuencias de comandos en la página pueden tener comportamientos diferentes. Si el script está marcado como "asíncrono" o "pendiente", el navegador puede continuar procesando el documento y simplemente ejecutar el script cuando esté disponible. Si el script no está marcado como asíncrono o diferido, el navegador DEBE dejar de procesar el documento hasta que el script se cargue y ejecute. Dichos scripts se denominan "bloqueo" porque impiden que el navegador continúe procesando el documento.

El documento HTML se divide en dos partes. El título del documento <head> se encuentra al principio y contiene hojas de estilo, scripts y otras instrucciones del navegador necesarias para mostrar el contenido. Después de que el encabezado es el cuerpo del documento <body>, contiene el contenido real que se muestra en la ventana del navegador (aunque los scripts y las hojas de estilo también pueden estar en el cuerpo). Hasta que el navegador llegue al cuerpo del documento, el usuario no tiene nada que mostrar y la página permanece en blanco. Por lo tanto, es importante procesar el encabezado lo más rápido posible. Si está interesado en los detalles, el sitio web HTML5 Rocks tiene un excelente tutorial sobre cómo funcionan los navegadores.

El navegador generalmente es responsable del orden en que se cargan los diversos recursos necesarios para crear la página y procesar el documento. En HTTP / 1.x, existen restricciones sobre cuántos objetos puede solicitar el navegador a cualquier servidor a la vez (generalmente 6 conexiones y solo un recurso a la vez por conexión), por lo que el navegador controla estrictamente el orden de las solicitudes. En HTTP / 2, la situación es completamente diferente. El navegador puede solicitar todos los recursos a la vez (al menos tan pronto como se entere de ellos) y proporciona al servidor instrucciones detalladas sobre cómo entregar estos recursos.

Óptimo orden de carga de recursos


Para la mayoría de las partes, existe un orden óptimo en el ciclo de carga de la página que maximiza la disponibilidad de la página para el usuario (y la diferencia entre el orden de carga óptimo y el no óptimo puede alcanzar el 50% o más).

Como se describió anteriormente, antes de que el navegador pueda mostrar cualquier contenido, CSS y JavaScript lo bloquean en la sección <head> . En esta etapa, es más rentable usar el 100% del canal para cargar recursos de bloqueo, en lugar de cargarlos en orden, ya que están escritos en el código HTML. Esto permite al navegador analizar y ejecutar cada elemento mientras carga el siguiente recurso de bloqueo, lo que crea una canalización óptima.



El tiempo de carga del script para la carga paralela o secuencial no difiere, pero para la carga secuencial, el primer script puede procesarse y ejecutarse durante la segunda carga.

Después de cargar recursos de bloqueo, la situación se vuelve un poco más interesante. Aquí, la carga óptima puede depender de un sitio en particular o incluso de las prioridades comerciales (selección de contenido generado por el usuario o publicidad, o análisis, etc.). Otro problema con las fuentes, porque el navegador detecta las fuentes deseadas después de aplicar la hoja de estilo al contenido mostrado. Por lo tanto, para cuando el navegador descubra la fuente, es necesario mostrar el texto que ya está listo para mostrarse en la pantalla. Cualquier retraso en la carga de la fuente no da como resultado texto en la pantalla (o el texto se muestra en la fuente incorrecta).

Como regla general, se deben considerar algunas compensaciones:

  • Las fuentes e imágenes personalizadas en la parte visible de la página (ventana gráfica) deben cargarse lo más rápido posible. Afectan directamente la experiencia visual del usuario al cargar la página.
  • JavaScript sin bloqueo debe cargarse secuencialmente con respecto a otros recursos de JavaScript para que cada uno de ellos pueda ser canalizado. JavaScript puede incluir lógica de aplicación personalizada, así como rastreadores para análisis y marketing, y su retraso puede conducir a una disminución en los indicadores rastreados por la empresa.
  • Las imágenes se pueden cargar en paralelo. Los primeros bytes del archivo de imagen contienen su tamaño, que puede ser necesario para el diseño del navegador, y la carga paralela de imágenes progresivas puede proporcionar integridad visual después de transferir aproximadamente el 50% del volumen total.

Dadas las compensaciones, en la mayoría de los casos, esta estrategia funciona bien:

  • Las fuentes personalizadas se descargan secuencialmente y comparten el ancho de banda disponible con imágenes dentro del alcance.
  • Las imágenes visibles se cargan en paralelo, compartiendo entre ellas la parte del ancho de banda asignado a ellas.
  • Cuando no hay más fuentes o imágenes visibles:
    • Las secuencias de comandos sin bloqueo se cargan secuencialmente y comparten el ancho de banda disponible con imágenes invisibles (que están fuera del alcance).
    • Las imágenes invisibles se cargan en paralelo, compartiendo entre ellas la parte del ancho de banda asignado a ellas.

Por lo tanto, el contenido visible para el usuario se carga lo más rápido posible, la lógica de la aplicación se retrasa al mínimo y las imágenes invisibles se cargan de tal manera que se complete el diseño lo más rápido posible.

Ejemplo


Para ilustrar, use una página de categoría de producto simplificada de un sitio de comercio electrónico típico:

  • Azul : archivo HTML de la página en sí.
  • Verde : una hoja de estilo externa (archivo CSS).
  • Naranja : cuatro scripts externos (JavaScript). Dos scripts de bloqueo en la parte superior de la página y dos asíncronos. Las secuencias de comandos de bloqueo se muestran en un tono más oscuro de naranja.
  • El rojo es una fuente web personalizada.
  • Violeta - 13 imágenes. El logotipo de la página y las cuatro imágenes del producto se muestran en la ventana de visualización, otras 8 imágenes del producto requieren desplazamiento. Las cinco imágenes visibles están indicadas por un tono más oscuro de púrpura.

Para simplificar, suponga que todos los recursos tienen el mismo tamaño y cada carga en 1 segundo. La descarga de todos los recursos lleva un total de 20 segundos, pero el orden y el método de carga son extremadamente importantes.



Así se verá la carga óptima de recursos en un navegador:



  • La página está en blanco durante los primeros 4 segundos mientras carga HTML, CSS y scripts de bloqueo: todos usan una conexión 100%.
  • En la marca de 4 segundos, el fondo y la estructura de la página se muestran sin texto o imágenes.
  • Después de un segundo, alrededor de 5 segundos, se muestra el texto de la página.
  • En el intervalo de 5-10 segundos, las imágenes se descargan, borrosas al principio, pero muy rápidamente se vuelven claras. Aproximadamente a los 7 segundos, el resultado es casi indistinguible de la versión final.
  • Aproximadamente 10 segundos, se completa la carga de todo el contenido visual en la parte visible de la página.
  • Durante los siguientes dos segundos, se carga y ejecuta JavaScript asíncrono, ejecutando cualquier lógica no crítica (análisis, etiquetas de marketing, etc.).
  • Durante los últimos 8 segundos, las imágenes restantes se cargan en caso de que el usuario se desplace por la página.

Priorización actual del navegador


Todos los motores de navegador actuales implementan varias estrategias de priorización , ninguna de las cuales es óptima.

Microsoft Edge e Internet Explorer no admiten la priorización , por lo que funcionan con la configuración predeterminada de HTTP / 2, que carga todo en paralelo, distribuyendo uniformemente el ancho de banda entre todos los recursos. Microsoft Edge en futuras versiones cambiará a usar el motor Chromium, lo que puede mejorar la situación. Pero por ahora, en nuestro ejemplo, el navegador se atasca la mayor parte del tiempo en el encabezado de la página, ya que las imágenes ralentizan la transmisión de scripts de bloqueo y hojas de estilo.



Visualmente, esto lleva a una experiencia bastante dolorosa: el usuario mira una pantalla en blanco durante 19 segundos, y luego hay un retraso de 1 segundo para mostrar el texto. Cuando vea la animación a continuación, tenga paciencia, porque durante 19 segundos puede parecer que no sucede nada en una pantalla en blanco (aunque es así):



Safari carga todos los recursos en paralelo , compartiendo el ancho de banda según su importancia, de acuerdo con Safari (los recursos de bloqueo como los scripts y las hojas de estilo son más importantes que las imágenes). Las imágenes se cargan en paralelo, pero también simultáneamente con contenido de bloqueo.



Aunque Safari es similar a Edge en el sentido de que todo se carga al mismo tiempo, asignar más ancho de banda para bloquear recursos le permite mostrar contenido mucho antes:



  • Después de aproximadamente 8 segundos, se completa la carga de la hoja de estilo y los scripts, por lo que puede comenzar a representar la página. Como las imágenes se cargaron en paralelo, también se pueden mostrar parcialmente (borrosas para imágenes progresivas). Esto sigue siendo el doble de lento que el escenario óptimo, pero mucho mejor que en Edge.
  • Después de unos 11 segundos, se carga la fuente. Puedes mostrar el texto. En este punto, se están cargando más datos para las imágenes, y se están volviendo un poco más nítidos. Esto es comparable a la situación alrededor de la marca de 7 segundos para un escenario de carga óptimo.
  • Durante los 9 segundos restantes, las imágenes se vuelven más nítidas a medida que se descargan más datos hasta que, finalmente, el proceso se completa en 20 segundos.

Firefox crea un árbol de dependencia que agrupa los recursos y luego planifica los grupos para cargar uno tras otro o compartir el ancho de banda entre los grupos. Dentro de este grupo, los recursos comparten ancho de banda y se cargan simultáneamente. Se planea que las imágenes se carguen después de las hojas de estilo que bloquean la representación y se cargan en paralelo, pero los scripts y las hojas de estilo que bloquean la representación también se cargan en paralelo y no se benefician de la canalización.



En nuestro ejemplo, esto sucede un poco más rápido que en Safari, ya que las imágenes están esperando que se cargue la hoja de estilo:



  • Aproximadamente 6 segundos, el contenido de la página original se muestra con un fondo y versiones borrosas de las imágenes del producto (en comparación con 8 segundos para Safari y 4 segundos en el mejor de los casos).
  • A los 8 segundos, la fuente se cargó y puede mostrar el texto junto con imágenes ligeramente más nítidas del producto (en comparación con los 11 segundos y 7 segundos de Safari en el mejor de los casos).
  • Durante los 12 segundos restantes, las imágenes se vuelven más nítidas a medida que se carga el contenido restante.

Chrome (y todos los navegadores basados ​​en Chromium) prioriza el inventario. Esto funciona muy bien para bloquear recursos que se cargan de manera óptima en orden, pero no tan buenos para las imágenes. Cada imagen se carga hasta el 100% antes de comenzar la siguiente.



En la práctica, este es un escenario de descarga casi óptimo, con la única diferencia de que las imágenes se descargan de una en una, y no en paralelo:



  • Hasta 5 segundos, cargar Chrome es idéntico al escenario óptimo, mostrando el fondo en el cuarto segundo y el contenido de texto en el quinto.
  • Durante los siguientes 5 segundos, las imágenes del área de visibilidad se cargan de una en una hasta que el proceso se completa en aproximadamente 10 segundos (en comparación con el escenario óptimo, cuando se muestran de forma ligeramente borrosa en aproximadamente 7 segundos y se vuelven más nítidas durante los tres segundos restantes).
  • Después de completar la parte visual de la página en 10 segundos (idéntico al escenario óptimo), los 10 segundos restantes se gastan en ejecutar scripts asincrónicos y cargar imágenes ocultas (al igual que en el escenario óptimo).

Comparación visual


La diferencia visual es bastante diferente, aunque técnicamente cargar todo el contenido lleva el mismo tiempo:



Priorización del lado del servidor


El cliente (navegador) solicita la priorización de HTTP / 2, y el servidor debe decidir qué hacer en función de la solicitud. Una gran cantidad de servidores no admiten esta función en absoluto , y el resto cumple con una solicitud del cliente. Otra opción es decidir la mejor priorización del lado del servidor según la solicitud del cliente.

De acuerdo con la especificación , la priorización HTTP / 2 es un árbol de dependencia que requiere un conocimiento completo de todas las solicitudes actuales para poder priorizar los recursos entre sí. Esto le permite implementar estrategias increíblemente complejas, pero es difícil implementarlo bien en el lado del navegador o del servidor (como lo demuestran varias estrategias del navegador y diferentes niveles de soporte del servidor). Para simplificar la gestión de priorización, hemos desarrollado un esquema más simple que todavía tiene toda la flexibilidad necesaria para una planificación óptima.

El esquema de priorización de Cloudflare consta de 64 "niveles" prioritarios, y dentro de cada nivel hay grupos de recursos que determinan cómo dividir la conexión entre ellos:



Primero, todos los recursos se descargan en un nivel de prioridad más alto, luego se realiza una transición a un nivel más bajo.

Dentro de un nivel de prioridad dado, hay tres grupos de concurrencia diferentes:

  • 0 : todos los recursos en el grupo "0" se envían secuencialmente en el orden en que se solicitaron utilizando 100% de ancho de banda. Solo después de cargar todos los recursos del grupo "0" se consideran otros grupos en el mismo nivel.
  • 1 : todos los recursos en el grupo de concurrencia "1" se envían secuencialmente en el orden en que se solicitaron. El ancho de banda disponible se distribuye uniformemente entre el grupo de paralelismo "1" y el grupo de paralelismo "n".
  • n : los recursos en el grupo de concurrencia "n" se transmiten en paralelo, compartiendo el ancho de banda disponible.

En la práctica, el grupo de paralelismo "0" es útil para contenido crítico que necesita ser procesado secuencialmente (scripts, CSS, etc.). El grupo "1" es útil para contenido menos importante que puede compartir el ancho de banda con otros recursos, pero donde los recursos mismos se benefician del procesamiento secuencial (scripts asíncronos, imágenes no progresivas, etc.). El grupo de concurrencia "n" es útil para los recursos que se benefician del procesamiento paralelo (imágenes progresivas, video, audio, etc.).

Priorización predeterminada de Cloudflare


Con la opción de priorización avanzada, se implementa el orden "óptimo" de carga de recursos, descrito anteriormente. Las prioridades específicas aplicadas son las siguientes:



Este esquema le permite enviar secuencialmente recursos que bloquean la representación, luego enviar imágenes visibles en paralelo y luego el resto del contenido de la página con algún nivel de uso compartido de ancho de banda para equilibrar la carga de la aplicación y el contenido. La advertencia * If Detectable es que no todos los navegadores distinguen entre diferentes tipos de hojas de estilo y scripts, pero aún así será mucho más rápido en todos los casos. La aceleración del 50%, especialmente para los visitantes de Edge y Safari, no será nada inusual:



Establecer prioridades con los trabajadores


El trabajo predeterminado más rápido es excelente, pero se vuelve realmente interesante gracias a la capacidad de configurar la priorización con el soporte de Cloudflare Workers, por lo que los sitios pueden redefinir la prioridad predeterminada para los recursos o implementar sus propios esquemas de priorización.

Si el trabajador agrega el encabezado de cf-priority a la respuesta, los servidores perimetrales de Cloudflare aplicarán la prioridad y concurrencia especificadas. El formato del encabezado es <priority> / <concurrency>, por lo tanto, el response.headers.set('cf-priority', “30/0”); establecerá la prioridad 30 y el paralelismo 0 para esta respuesta. Del mismo modo, "30/1" establecerá el paralelismo "1" y "30 / n" establecerá el paralelismo en n.

Con tal flexibilidad, un sitio puede establecer una prioridad arbitraria de recursos para sus necesidades. Por ejemplo, para aumentar la prioridad de algunos scripts asíncronos o imágenes principales importantes: se descargan incluso antes de que el navegador determine que están dentro del alcance.

Para informar sobre las decisiones de priorización, el tiempo de ejecución de los trabajadores también indica la información solicitada por el navegador sobre la priorización en el objeto de solicitud, que se pasa al receptor de los eventos del trabajador (request.cf.requestPriority). Las prioridades entrantes son una lista de atributos separados por punto y coma. Se parece a esto: weight=192;exclusive=0;group=3;group-weight=127 .

  • peso : peso para priorizar HTTP / 2.
  • exclusivo : el distintivo HTTP / 2 exclusivo (1 para navegadores basados ​​en Chromium, 0 para otros).
  • group : identificador de flujo HTTP / 2 para el grupo de solicitud (distinto de cero para Firefox).
  • ponderación de grupo : ponderación HTTP / 2 para el grupo de solicitudes (no es cero para Firefox).

Esto es solo el comienzo.


La capacidad de configurar y controlar la prioridad de las respuestas es el principal componente para un gran trabajo futuro. Tenemos la intención de presentar nuestras propias optimizaciones avanzadas además de esto, pero con el apoyo de los trabajadores, todos los sitios e investigadores pueden experimentar con diversas estrategias de priorización. A través del Mercado de aplicaciones, las empresas también pueden crear nuevos servicios de optimización sobre la plataforma de trabajo y ponerlos a disposición de otros sitios.

Si tiene un plan Pro o superior, vaya a la pestaña "Velocidad" en el panel de control de Cloudflare y habilite la "priorización HTTP / 2 avanzada" para acelerar su sitio.

Source: https://habr.com/ru/post/452020/


All Articles